2016-08-18 23 views
8

Tôi đã kêu lách cách để xác nhận ví dụ trên trang 91 của Hiệu quả hiện đại C++, và tôi gặp phải vấn đề có vẻ lạ. Đây đangC++ tuyên bố không chấp nhận thay đổi khoản khấu trừ mẫu

template<typename C> 
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) { 
    std::cout << "container version" << std::endl; 
} 

template<> 
void doStuff<int>(int& x, int& y) noexcept { 
    std::cout << "int version" << std::endl; 
} 

int main() { 
    vector<int> v1 = {1, 2, 3}; 
    vector<int> v2 = {4, 5, 6}; 
    int x = 5; 
    int y = 6; 
    doStuff(x, y); 
    doStuff(v1, v2); 
} 

Cung cấp cho tôi một lỗi như

error: request for member ‘front’ in ‘a’, which is of non-class type ‘int’ void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) {

Vì vậy, nó có vẻ như phiên bản đầu doStuff đang được gọi là, mặc dù a.front() và b.front() nên trả về các tham chiếu đến ints. Nếu tôi loại bỏ tất cả các khai báo không nhận biết từ mã, tôi nhận được kết quả mong đợi.

Điều này là với gcc 5.4.

Tôi đang làm gì sai?

Cảm ơn

+1

Hãy nhớ rằng các mẫu là một tính năng biên dịch. Khi phiên bản đầu tiên của hàm 'doStuff' được định nghĩa, trình biên dịch không biết gì về chuyên môn cho' int'. –

+1

Ngoài ra còn có vấn đề 'doStuff' chưa được khai báo khi nó được sử dụng lần đầu trong đặc tả' noexcept'. – aschepler

Trả lời

7

Vấn đề là, khi tra cứu tên vào thời điểm này:

template<typename C> 
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(a.front(), b.front()))) { 
//           ^^^^^^^ 

sẽ chỉ tìm thấy một doStuff(): template chức năng của bạn. Chuyên môn chưa được khai báo, vì vậy nó không được xem xét.

Điều đầu tiên cần làm là chỉ cần tránh các chuyên môn. Họ đang vụng về. Nhưng sau đó sửa chữa thực sự sẽ được dính vào một loại thêm trống chỉ dành cho mục đích tra cứu phụ thuộc vào đối số. Thao tác này sẽ thêm tên phụ thuộc vào tra cứu noexcept sẽ trì hoãn lời gọi cho đến khi khởi tạo:

namespace N { 
    struct adl { }; 

    void doStuff(adl, int& , int&) noexcept { 
     std::cout << "int version" << std::endl; 
    } 

    template<typename C> 
    void doStuff(adl, C& a, C& b) noexcept(noexcept(doStuff(adl{}, a.front(), b.front()))) { 
     std::cout << "container version" << std::endl; 
    } 
} 

template <class C> 
void doStuff(C& a, C& b) noexcept(noexcept(doStuff(N::adl{}, a, b))) 
{ 
    doStuff(N::adl{}, a, b); 
} 
+1

Quá tải 'int' có thực sự phải là đầu tiên không? Không phải 'doStuff' trong ký hiệu' noexcept' là một biểu tượng phụ thuộc, có nghĩa là nó không được giải quyết cho đến khi instantiation? – John

+0

Có lẽ đáng ngạc nhiên, điều này sẽ thất bại cho 'C = std :: vector >'. – aschepler

+0

@aschepler Đã sửa lỗi. – Barry

2

Chuyên môn mẫu không bị quá tải. Chuyên môn của bạn cho doStuff<int> không phải là quá tải của doStuff<C>, đó là chuyên môn. Vì vậy, quá tải độ phân giải không xem xét nó, mẫu instantiation sẽ xem xét nó, nếu bản gốc được chọn bởi quá tải độ phân giải. Thay thế chuyên môn của bạn với một tình trạng quá tải (không phải mẫu, lấy hai int& s)

void doStuff(int& a, int& b) noexcept; 
Các vấn đề liên quan