2013-02-11 7 views
14

Chương trình sau đây, khi được biên dịch bằng GCC 4.7 và clang 3.2, tạo ra "1" làm đầu ra.Tại sao việc xác nhận quyền sở hữu is_constructible có thể được cấu hình khi nó không được?

#include <type_traits> 

struct foo { 
    template<typename T> 
    foo(T) { 
     static_assert(not std::is_same<int, T>(), "no ints please"); 
    } 
}; 

#include <iostream>  
int main() { 
    std::cout << std::is_constructible<foo, int>(); 
} 

Điều này gây nhầm lẫn. foo là khá rõ ràng không thể xây dựng từ int! Nếu tôi thay đổi main đến sau, cả hai trình biên dịch từ chối nó do sự khẳng định tĩnh không:

int main() { 
    foo(0); 
} 

Tại sao cả hai trình biên dịch nói nó là constructible?

+0

Bạn nên sử dụng enable_if để xóa ints khỏi các foos có thể. – PlasmaHH

Trả lời

21

Đây là những gì tiêu chuẩn có nói (§20.9.5/6), với sự nhấn mạnh của tôi:

Với chức năng nguyên mẫu sau:

template <class T> 
typename add_rvalue_reference<T>::type create(); 

điều kiện vị cho một mẫu chuyên môn is_constructible<T, Args...> phải được thỏa mãn nếu và chỉ khi định nghĩa biến sau đây sẽ được tạo đúng cho một số được phát minh biến t:

T t(create<Args>()...); 

[Lưu ý: Các mã thông báo này không bao giờ được hiểu là hàm khai báo . -end lưu ý]

Tiếp cận kiểm tra được thực hiện như thể trong một bối cảnh không liên quan đến T và bất kỳ Args. Chỉ xem xét tính hợp lệ của bối cảnh ngay lập tức của việc khởi tạo biến số . [Lưu ý: Việc đánh giá việc khởi tạo có thể dẫn đến các tác dụng phụ chẳng hạn như việc thực hiện các mẫu chuyên môn và chức năng mẫu lớp , tạo các hàm được xác định ngầm định, v.v. Các hiệu ứng bên hông không nằm trong “ngữ cảnh tức thời” và có thể dẫn đến chương trình bị lỗi. -end lưu ý]

Khẳng định chỉ thất bại khi các mẫu nhà xây dựng được khởi tạo. Tuy nhiên, như được xóa trong ghi chú, xác nhận đó không nằm trong bối cảnh ngay lập tức của định nghĩa biến được xem xét và do đó không ảnh hưởng đến "hiệu lực" của nó. Vì vậy, các trình biên dịch có thể đếm định nghĩa đó là hợp lệ, và do đó tuyên bố rằng foo thực sự là có thể xây dựng từ int, ngay cả khi thực sự cố gắng xây dựng một sốtừ một kết quả không đúng định dạng.

Lưu ý rằng các trình biên dịch cũng được phép thay vì có is_constructible mang lại giá trị sai, chỉ từ chối chương trình gốc dựa trên xác nhận, mặc dù cả hai đều không thực hiện.

+10

Lưu ý rằng nó chỉ có ý nghĩa. 'std :: is_constructible <>' xác định xem có một hàm tạo lấy đối số, không phải là hàm tạo được định nghĩa đúng. Hãy xem xét rằng định nghĩa của hàm tạo (và do đó 'static_assert') không cần phải được hiển thị cho trình biên dịch khi xử lý' is_constructible'. Nếu bạn muốn khởi động, hãy sử dụng SFINAE để vô hiệu hóa hàm tạo đó cho 'int', hoặc thêm' foo (int) = delete; 'sẽ đánh dấu hàm khởi tạo là không có sẵn (không chắc chắn 100% phương pháp cuối cùng này, nhưng nó sẽ hoạt động) –

5

foo2 là số foo của bạn. foo1 là một foo làm những gì bạn muốn foo của bạn để làm.

#include <type_traits> 
#include <utility> 

struct foo1 { 
    template<typename T,typename=typename std::enable_if< !std::is_same<int, T>::value >::type> 
    foo1(T) { 
    static_assert(not std::is_same<int, T>(), "no ints please"); 
    } 
}; 
struct foo2 { 
    template<typename T> 
    foo2(T) { 
    static_assert(not std::is_same<int, T>(), "no ints please"); 
    } 
}; 

#include <iostream>  
int main() { 
    std::cout << std::is_constructible<foo1, int>(); 
    std::cout << std::is_constructible<foo2, int>(); 
} 
+0

@ R.MartinhoFernandes chỉ muốn có câu trả lời để ở đây trong trường hợp ai đó truy cập câu hỏi trên google. :) – Yakk