2009-10-26 7 views
17

Sau khi trả lời this câu hỏi Tôi đã cố gắng tìm mẫu is_complete trong thư viện Tăng cường và tôi nhận ra rằng không có mẫu nào trong Boost.TypeTraits. Tại sao không có mẫu như vậy trong thư viện Boost? Nó trông như thế nào?Làm thế nào để viết mẫu `is_complete`?

//! Check whether type complete 
template<typename T> 
struct is_complete 
{ 
    static const bool value = (sizeof(T) > 0); 
}; 

... 

// so I could use it in such a way 
BOOST_STATIC_ASSERT(boost::is_complete<T>::value); 

Đoạn mã trên là không đúng, bởi vì nó là bất hợp pháp để áp dụng sizeof đến một loại không đầy đủ. Điều gì sẽ là một giải pháp tốt? Có thể áp dụng SFINAE trong trường hợp này không?


Vâng, vấn đề này không thể giải quyết nói chung mà không vi phạm các ODR rule, nhưng có là có một nền tảng cụ thể solution mà làm việc cho tôi.

+10

tôi nghĩ rằng đây có thể không làm việc theo nguyên tắc (trừ khi bạn luôn áp dụng nó cho một loại luôn luôn không đầy đủ, hoặc luôn luôn hoàn thành). Bởi vì liệu 'U' có hoàn thành hay không,' is_complete 'luôn luôn chỉ định cùng một loại. Nếu bây giờ bạn đi và sử dụng 'is_complete ' trong hai đơn vị dịch khác nhau, thành viên giá trị có thể có giá trị khác nhau mỗi lần và trình biên dịch miễn phí với giá trị nào nó sử dụng. Đây không phải là hợp lệ tôi nghĩ, nhưng tôi không thể tìm thấy một tuyên bố của các tiêu chuẩn về việc này: (Sẽ được vui mừng nếu bạn có thể tìm hiểu. –

+0

Câu hỏi lớn hơn là lý do tại sao. Thời gian biên dịch –

+0

Cũng không có cách nào trong C++ 03 điều này có thể được thực hiện.Đầu tiên với C++ 0x với "sfinae cho biểu thức", nhưng thậm chí nếu bạn chuyển 'vector ' cho ví dụ và 'vector' chỉ được khai báo nhưng không được xác định, sau đó kiểm tra tính đầy đủ sẽ mang lại cho một sự khởi tạo ngầm định, và nếu định nghĩa không có sẵn sẽ phát hành một lỗi cứng mà không được bao hàm bởi sfinae (lỗi không nằm trong "bối cảnh ngay lập tức") –

Trả lời

11

Câu trả lời được đưa ra bởi Alexey Malistov có thể được sử dụng trên MSVC với một sửa đổi nhỏ:

namespace 
{ 
    template<class T, int discriminator> 
    struct is_complete { 
     static T & getT(); 
     static char (& pass(T))[2]; 
     static char pass(...); 
     static const bool value = sizeof(pass(getT()))==2; 
    }; 
} 
#define IS_COMPLETE(X) is_complete<X,__COUNTER__>::value 

Thật không may, COUNTER vĩ mô được xác định trước không phải là một phần của tiêu chuẩn, vì vậy nó sẽ không hoạt động trên mọi trình biên dịch.

+0

' __COUNTER__' được hỗ trợ bởi MSVC và với gcc bắt đầu từ 4.3. Tuy nhiên, với gcc, vấn đề không thực sự trong việc tạo ra các kiểu 'is_complete' khác nhau với sự trợ giúp của' __COUNTER__' nhưng có trình biên dịch thực hiện SFINAE và chọn quá tải 'pass (...)'. –

+0

Điều này dường như không tương thích với các loại trừu tượng hoàn chỉnh. Trên MSVC 2008 'IS_COMPLETE (MyAbstractClass)' không biên dịch được. Và ngay cả khi trình biên dịch đã cho phép điều này và có thể thực hiện SFINAE với kiểu trừu tượng, có vẻ như nó sẽ trả lời sai (tức là nó sẽ báo cáo rằng một kiểu trừu tượng hoàn chỉnh là không hoàn chỉnh). – bshields

+3

Điều này vi phạm quy tắc ODR. Bộ đếm sẽ cho bạn nhiều chuyên môn khác nhau trong một đơn vị dịch, nhưng nếu bạn sử dụng nó trong một tệp thứ hai, cùng một chuyên môn sẽ nhận được một định nghĩa không tương thích. Giải pháp là đặt nó vào một không gian tên không tên. – Potatoswatter

0

Tôi không thể tìm thấy bất kỳ điều gì trong tiêu chuẩn đảm bảo rằng kích thước trên loại không đầy đủ sẽ mang lại 0. Tuy nhiên, yêu cầu rằng nếu T chưa hoàn thành tại một thời điểm nào đó, nhưng hoàn thành sau trong đơn vị dịch đó, các tham chiếu đến T tham chiếu đến cùng một loại - vì vậy khi tôi đọc nó, ngay cả khi T chưa hoàn thành, trong đó mẫu của bạn được gọi, nó sẽ được yêu cầu để nói nó hoàn thành nếu T được hoàn thành ở đâu đó trong đơn vị dịch đó.

+0

"_that nếu T chưa hoàn thành tại một thời điểm nào đó, nhưng hoàn thành sau trong đơn vị dịch đó, tất cả các tham chiếu đến T đều tham chiếu cùng loại_" thực sự, loại người dùng xác định không ** hoàn thành hoặc chưa hoàn thành **, chỉ là không đầy đủ cho đến khi nó được hoàn thành ('void' vốn đã không đầy đủ). Vì vậy, toàn bộ ý tưởng là cực kỳ có vấn đề. – curiousguy

+1

'sizeof' bị hỏng cho các loại không đầy đủ và không thể mang lại số không, vì C++ 98:" Toán tử 'sizeof' không được được áp dụng cho một biểu thức có ... loại chưa đầy đủ, ... hoặc tên được đặt dấu ngoặc đơn như vậy các loại. " Điều trị của GCC là không phù hợp, nhưng tốt hơn hết là nên hỗ trợ nó nếu viết một cơ sở như vậy. – Potatoswatter

7
template<class T> 
struct is_complete { 
    static T & getT(); 
    static char (& pass(T))[2]; 
    static char pass(...); 

    static const bool value = sizeof(pass(getT()))==2; 
}; 
+4

Đẹp, nhưng như @litb nói trong bình luận của mình, nó không hoạt động đúng nếu is_complete xuất hiện ở 2 vị trí mâu thuẫn trong cùng một tệp, khi định nghĩa loại lớp xuất hiện giữa chúng (tôi đã thử :)). – Asaf

+0

không thành công ở đây có lỗi: khởi tạo đối số 1 của 'static char (& is_complete :: pass (T)) [2] [with T = Foo]' –

+0

compomp with gcc 4.4.2 –

2

Tôi e rằng bạn không thể triển khai các đặc điểm loại is_complete như vậy. Việc thực hiện do @Alexey thất bại trong việc biên dịch trên G ++ 4.4.2 và G ++ 4.5.0:

error: initializing argument 1 of ‘static char (& is_complete::pass(T))[2] [with T = Foo]’

Trên Mac của tôi, với G ++ 4.0.1 đánh giá is_complete<Foo>::value nơi struct Foo; là sản lượng không đầy đủ để true mà thậm chí còn tồi tệ hơn lỗi trình biên dịch.

T có thể vừa đầy đủ vừa không hoàn chỉnh trong cùng một chương trình, tùy thuộc vào đơn vị dịch nhưng nó luôn là cùng loại. Kết quả là, như đã nhận xét ở trên, is_complete<T> luôn là cùng loại.

Vì vậy, nếu bạn tôn trọng ODR, không thể có is_complete<T> đánh giá các giá trị khác nhau tùy thuộc vào vị trí được sử dụng; nếu không nó có nghĩa là bạn có các định nghĩa khác nhau cho is_complete<T> mà ODR cấm.

EDIT: Là câu trả lời được chấp nhận, bản thân tôi đã tấn công xung quanh giải pháp sử dụng macro __COUNTER__ để tạo một loại khác nhau is_complete<T, int> mỗi lần sử dụng macro IS_COMPLETE. Tuy nhiên, với gcc, tôi không thể giúp SFINAE làm việc ngay từ đầu.

2

Giải quyết việc này yêu cầu thực hiện tính toán trong đối số mặc định của mẫu tra cứu, khi cố thay đổi định nghĩa mẫu vi phạm quy tắc ODR (mặc dù kết hợp __COUNTER__namespace {} có thể hoạt động xung quanh ODR).

Điều này được viết bằng C++ 11 nhưng có thể được điều chỉnh để hoạt động ở chế độ C++ 03 của trình biên dịch tương thích C++ 11 gần đây vừa phải.

template< typename t > 
typename std::enable_if< sizeof (t), std::true_type >::type 
is_complete_fn(t *); 

std::false_type is_complete_fn(...); 

template< typename t, bool value = decltype(is_complete_fn((t *) nullptr))::value > 
struct is_complete : std::integral_constant< bool, value > {}; 

Online demo.

Đối số mặc định được đánh giá nơi mẫu được đặt tên, vì vậy nó theo ngữ cảnh có thể chuyển đổi giữa các định nghĩa khác nhau. Không cần phải có một chuyên môn và định nghĩa khác nhau ở mỗi lần sử dụng; bạn chỉ cần một cho true và một cho false.

Nguyên tắc được đưa ra trong §8.3.6/9, trong đó áp dụng như nhau đối với đối số chức năng mặc định và mặc định template-đối số:

Default arguments are evaluated each time the function is called.

Nhưng hãy cẩn thận, sử dụng này bên trong một mẫu là gần như chắc chắn vi phạm ODR. Một mẫu được khởi tạo trên một kiểu không hoàn chỉnh không được làm bất cứ điều gì khác biệt nếu nó được khởi tạo trên một kiểu hoàn chỉnh. Cá nhân tôi chỉ muốn điều này cho một số static_assert.

Ngẫu nhiên, nguyên tắc này cũng có thể hữu ích nếu bạn muốn đi theo cách khác và implement chức năng của __COUNTER__ bằng cách sử dụng mẫu và quá tải.

-1

My 5 cent:

template <typename T, typename = void> 
    struct is_incomplete : ::std::true_type 
    { 
    }; 

    template <typename T> 
    struct is_incomplete<T, decltype(sizeof(T))> : ::std::false_type 
    { 
    }; 

    template <> 
    struct is_incomplete<void> : ::std::false_type 
    { 
    }; 
+0

Để điều này hoạt động chính xác, tôi phải thực hiện một số chỉnh sửa: 'chi tiết không gian tên { mẫu struct is_incomplete :: :: std :: true_type {}; mẫu struct is_incomplete : :: std :: false_type {}; } mẫu struct is_incomplete: detail :: is_incomplete :: type {}; ' – ScootyPuff

+0

@ScootyPuff Thực hiện chỉnh sửa, scotty, tôi sẽ không bận tâm. – user1095108

+0

Điều này sẽ cho bạn biết lớp học chưa hoàn thành khi lần đầu tiên truy vấn, nhưng nó không được chẩn đoán (không có chẩn đoán và sẽ không hoạt động) nếu bạn sử dụng lại sau khi lớp được xác định. – Potatoswatter

5

Nó có thể là hơi muộn, nhưng cho đến nay, không có giải pháp C++ 11 làm việc cho cả hai loại đầy đủ và trừu tượng.

Vì vậy, đây là bạn.

Với VS2015 (v140), g ++> = 4.8.1, kêu vang> = 3,4, điều này đang làm việc:

template <class T, class = void> 
struct IsComplete : std::false_type 
{}; 

template <class T> 
struct IsComplete< T, decltype(void(sizeof(T))) > : std::true_type 
{}; 

Nhờ Bát-Ulzii Luvsanbat: https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/

Với VS2013 (V120) :

namespace Details 
{ 

    template <class T> 
    struct IsComplete 
    { 
     typedef char no; 
     struct yes { char dummy[2]; }; 

     template <class U, class = decltype(sizeof(std::declval<U>())) > 
     static yes check(U*); 

     template <class U> 
     static no check(...); 

     static const bool value = sizeof(check<T>(nullptr)) == sizeof(yes); 
    }; 

} // namespace Details 


template <class T> 
struct IsComplete : std::integral_constant< bool, Details::IsComplete<T>::value > 
{}; 

Cái này được lấy cảm hứng từ internets và static assert that template typename T is NOT complete?