2016-10-28 19 views
7

tôi đi qua với mã này trong một thực hiện std::optional:biểu thức kích thước này được đánh giá như thế nào? và tại sao nó được gọi như vậy?

template <class T, class U> 
struct is_assignable 
{ 
    template <class X, class Y> 
    constexpr static bool has_assign(...) { return false; } 

    template <class X, class Y, size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true)) > 
    // the comma operator is necessary for the cases where operator= returns void 
    constexpr static bool has_assign(bool) { return true; } 

    constexpr static bool value = has_assign<T, U>(true); 
}; 

Phần mà tôi không thể hiểu cách thức hoạt động hoặc làm thế nào nó được đánh giá là size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true)) Tôi biết rằng nếu hoạt động assign thất bại nó sẽ rơi trở lại định nghĩa đầu tiên của has_assign trả về false, nhưng tôi không biết tại sao nó có phần , true).

Tôi đã thực hiện một số thử nghiệm với cấu trúc trả về khoảng trống trên toán tử gán và xóa phần , true trong sizeof mang lại cho tôi kết quả tương tự.

+2

http://en.cppreference.com/w/cpp/language/sfinae. Nếu biểu thức bên trong 'sizeof' là hợp lệ cho' X' và 'Y' đã cho, thì có hai quá tải của' has_assign' và giá trị thứ hai được chọn cho 'has_assign (true)' như một kết hợp tốt hơn. Nếu biểu thức là vô nghĩa, thì tình trạng quá tải đó bị loại bỏ, và quá trình đầu tiên được chọn. –

+1

Một số trình biên dịch xác định 'sizeof (void) == 1' làm phần mở rộng. Bật thêm cảnh báo. – Quentin

+2

Sẽ trực tiếp hơn để chỉ viết 'class S = decltype (std :: declval () = std :: declval ())'. Chúng tôi không quan tâm những gì 'S' là anyway, chỉ là biểu thức đó là hợp lệ. – Barry

Trả lời

7

Để áp dụng sizeof(), bạn cần một loại hoàn chỉnh. Nhưng trở về một loại hoàn toàn không phải là một yêu cầu của assignability, do đó:

sizeof((std::declval<X>() = std::declval<Y>(), true)) 
     ~~~~~~~~~~~~~~~~~~ expr ~~~~~~~~~~~~~~~~~~~~~ 

nếu việc chuyển nhượng có hiệu lực trong hai loại, sau đó chúng tôi có sizeof(expr) nơi các loại exprbool (vì true). Vì vậy, nếu nhiệm vụ là hợp lệ, chúng tôi nhận được một số thực sự size. Nếu không, thay thế thất bại.


Nhưng đây là cách viết mã không cần thiết khó hiểu này. Hơn nữa, nó thậm chí không chính xác bởi vì tôi có thể viết một loại như:

struct Evil { 
    template <class T> Evil operator=(T&&); // assignable from anything 
    void operator,(bool);     // mwahahaha 
}; 

và bây giờ sizeof() vẫn không hoạt động.

Thay vào đó, thích đơn giản:

class = decltype(std::declval<X>() = std::declval<Y>()) 

này thực hiện giống kết quả - một trong hai thất bại thay hay không - mà không cần phải quan tâm ở tất cả về những gì loại của kết quả là hoặc để xử lý các trường hợp đặc biệt.

+0

Mã gốc có hoạt động trên một số trình biên dịch (có thể cũ hơn và không hoàn toàn phù hợp với tiêu chuẩn) không? Một lợi thế có thể có trong mã được tạo. Vì 'sizeof (true)' sẽ luôn cho cùng kích thước (giả sử tất cả các tệp được biên dịch với các tùy chọn tương thích), sau đó nó sẽ giúp đảm bảo rằng chỉ có một hàm được giữ nếu nội tuyến không được thực hiện ... – Phil1970

+0

@ Phil1970 Điều này được thực hiện tại thời gian biên dịch. Không có hàm nào trong số các hàm này xuất hiện trong tệp đối tượng. – Barry

+0

Chỉ cần một lưu ý, có một số trường hợp mà is_assignable (ngay cả một tiêu chuẩn) có thể cung cấp cho bạn một dương tính giả. Nhưng tôi nghĩ một câu hỏi mới có thể được đăng tải về vấn đề này. [gist] (https://gist.github.com/aindigo/69bf0ca558a127321af70dbb82314f5c) – alter

7

Về nguyên tắc, loại biểu thức std::declval<X>() = std::declval<Y>() (nghĩa là loại trả về của operator = có liên quan) có thể tùy ý, bao gồm loại không đầy đủ hoặc void. Trong trường hợp như vậy, SFINAE sẽ không khởi động, vì biểu thức là hợp lệ. Tuy nhiên, bạn sẽ gặp phải lỗi khi áp dụng sizeof cho loại không đầy đủ. (Lưu ý rằng một số trình biên dịch xác định sizeof(void) == 1 là một phần mở rộng, nhưng điều đó không thể được dựa trên toàn cầu).

Thêm , true sau khi biểu thức SFINAE sửa lỗi này bằng cách loại bỏ loại nhiệm vụ (bất kể nó là gì) và áp dụng sizeof đến true thay thế (hoàn toàn hợp lệ).

Như đã nêu bởi Barry trong các ý kiến, một cách tiếp cận trực tiếp hơn là nên sử dụng các loại phân trong decltype thay vì trong sizeof, như thế này:

template <class X, class Y, class S = decltype(std::declval<X>() = std::declval<Y>()) > 
constexpr static bool has_assign(bool) { return true; } 
Các vấn đề liên quan