2013-06-03 32 views
11

Cách xác định (trong tinh thần <type_traits>) có hay không một loại chuyển đổi rõ ràng thành loại khác? Ví dụ, tôi muốn kiểm tra sự hiện diện của F::explicit operator double const &() const; đối với một số class/structF, nhưng, cùng lúc đó, F không nên được một cách rõ ràng mui trần để float hoặc long double (cái gì đó như pred< double const & >::value && !pred<float>::value && !pred< long double >::value).kiểm tra xem loại có thể được chuyển đổi rõ ràng

Lưu ý, rằng std::is_convertible< From, To >::value kiểm tra "nếu Từ có thể được chuyển đổi sang Để sử dụng ngầm chuyển đổi". Nhưng tôi muốn xác định liệu có chuyển đổi rõ ràng toán tử hay không.

Và, nếu nó có thể, "làm thế nào để xác định, loại Từ là chuyển đổi thành một tài liệu tham khảo cụ thể làĐể?"?

+0

Có lẽ tôi hiểu nhầm câu hỏi, nhưng bạn có thể thực hiện 'dynamic_cast (FROM & val)', nó sẽ ném 'std :: bad_cast' nếu chuyển đổi không đủ điều kiện. –

+0

@ H2CO3 Giả sử, câu hỏi đó là về vấn đề thời gian biên dịch. – Orient

+2

Khi bạn muốn tìm kiếm toán tử chuyển đổi rõ ràng, nhiệm vụ của bạn dường như đun sôi xuống [xác định xem một lớp T có một hàm thành viên với một chữ ký đã cho] hay không (http://stackoverflow.com/q/87372/1362568). Có nhiều câu trả lời ở đó, kể cả một trong các câu trả lời của tôi. –

Trả lời

11

Bạn cần phải xác định riêng của bạn:

template <class U, class T> 
struct is_explicitly_convertible 
{  
    enum {value = std::is_constructible<T, U>::value && !std::is_convertible<U, T>::value}; 
}; 
7

tập kiểm tra của bạn là một liên kết off-site và đã thay đổi kể từ khi ban bài viết của bạn, vì vậy tôi sẽ sao chép nguyên văn bài kiểm tra-set tôi đang nói về ở đây:

static_assert(is_explicitly_convertible< double, double >::value, "1"); 
static_assert(is_explicitly_convertible< double &, double >::value, "2"); 
static_assert(is_explicitly_convertible< double const, double >::value, "3"); 
static_assert(is_explicitly_convertible< double const &, double >::value, "4"); 

static_assert(is_explicitly_convertible< double, double const & >::value, "5"); 
static_assert(is_explicitly_convertible< double &, double const & >::value, "6"); 
static_assert(is_explicitly_convertible< double const, double const & >::value, "7"); 
static_assert(is_explicitly_convertible< double const &, double const & >::value, "8"); 

static_assert(!is_explicitly_convertible< double, double & >::value, "9"); // not a ref 
static_assert(is_explicitly_convertible< double &, double & >::value, "10"); 
static_assert(!is_explicitly_convertible< double const, double & >::value, "11"); 
static_assert(!is_explicitly_convertible< double const &, double & >::value, "12"); 

static_assert(is_explicitly_convertible< double, double const >::value, "13"); 
static_assert(is_explicitly_convertible< double &, double const >::value, "14"); 
static_assert(is_explicitly_convertible< double const, double const >::value, "15"); 
static_assert(is_explicitly_convertible< double const &, double const >::value, "16"); 

static_assert(is_explicitly_convertible< AA const &, A const & >::value, "=&1.a"); 
static_assert(is_explicitly_convertible< CC const &, C const & >::value, "=&1.b"); 
static_assert(is_explicitly_convertible< BB const &, B const & >::value, "=&1.c"); 
static_assert(!is_explicitly_convertible< AA const &, A & >::value, "&1.a"); 
static_assert(!is_explicitly_convertible< CC const &, C & >::value, "&1.b"); 
static_assert(!is_explicitly_convertible< BB const &, B & >::value, "&1.c"); 

static_assert(is_explicitly_convertible< AA const, A const & >::value, "=1.a"); 
static_assert(is_explicitly_convertible< CC const, C const & >::value, "=1.b"); 
static_assert(is_explicitly_convertible< BB const, B const & >::value, "=1.c"); 
//static_assert(!is_explicitly_convertible< AA const, A >::value, "=2.a"); // ??????????????? 
//static_assert(!is_explicitly_convertible< CC const, C >::value, "=2.b"); 
//static_assert(!is_explicitly_convertible< BB const, B >::value, "=2.c"); 
static_assert(!is_explicitly_convertible< AA const, A & >::value, "=3.a"); // good! 
static_assert(!is_explicitly_convertible< CC const, C & >::value, "=3.b"); // 
static_assert(!is_explicitly_convertible< BB const, B & >::value, "=3.c"); // 
static_assert(!is_explicitly_convertible< AA const, A && >::value, "=4.a"); // not interesting 
static_assert(!is_explicitly_convertible< CC const, C && >::value, "=4.b"); // 
static_assert(!is_explicitly_convertible< BB const, B && >::value, "=4.c"); // 
static_assert(!is_explicitly_convertible< AA const, B const & >::value, "=5.a"); 
static_assert(!is_explicitly_convertible< AA const, C const & >::value, "=5.b"); 
static_assert(!is_explicitly_convertible< BB const, A const & >::value, "=5.c"); 
static_assert(!is_explicitly_convertible< BB const, C const & >::value, "=6.a"); 
static_assert(!is_explicitly_convertible< CC const, A const & >::value, "=6.b"); 
static_assert(!is_explicitly_convertible< CC const, B const & >::value, "=6.c"); 
static_assert(!is_explicitly_convertible< AA const, B & >::value, "=7.a"); 
static_assert(!is_explicitly_convertible< AA const, C & >::value, "=7.b"); 
static_assert(!is_explicitly_convertible< BB const, A & >::value, "=7.c"); 
static_assert(!is_explicitly_convertible< BB const, C & >::value, "=8.a"); 
static_assert(!is_explicitly_convertible< CC const, A & >::value, "=8.b"); 
static_assert(!is_explicitly_convertible< CC const, B & >::value, "=8.c"); 
static_assert(!is_explicitly_convertible< AA const, B >::value, "=9.a"); // very subtle moment (see class AA above) 
static_assert(!is_explicitly_convertible< AA const, C >::value, "=9.b"); 
static_assert(is_explicitly_convertible< BB const, A >::value == std::is_constructible< A, A && >::value, "=9.c"); // (see class BB above) 
static_assert(!is_explicitly_convertible< BB const, C >::value, "=10.a"); 
static_assert(!is_explicitly_convertible< CC const, A >::value, "=10.b"); 
static_assert(!is_explicitly_convertible< CC const, B >::value, "=10.c"); 

static_assert(is_explicitly_convertible< AA, A & >::value, "~1.a"); 
static_assert(is_explicitly_convertible< CC, C & >::value, "~1.b"); 
static_assert(is_explicitly_convertible< BB, B & >::value, "~1.c"); 
//static_assert(!is_explicitly_convertible< AA, A >::value, "~2.a"); // ??????????????? 
//static_assert(!is_explicitly_convertible< CC, C >::value, "~2.b"); 
//static_assert(!is_explicitly_convertible< BB, B >::value, "~2.c"); 
static_assert(is_explicitly_convertible< AA, A const & >::value, "~3.a"); // convertible 
static_assert(is_explicitly_convertible< CC, C const & >::value, "~3.b"); // 
static_assert(is_explicitly_convertible< BB, B const & >::value, "~3.c"); // 
static_assert(!is_explicitly_convertible< AA, B const & >::value, "~4.a"); 
static_assert(!is_explicitly_convertible< AA, C const & >::value, "~4.b"); 
static_assert(!is_explicitly_convertible< BB, A const & >::value, "~4.c"); 
static_assert(!is_explicitly_convertible< BB, C const & >::value, "~5.a"); 
static_assert(!is_explicitly_convertible< CC, A const & >::value, "~5.b"); 
static_assert(!is_explicitly_convertible< CC, B const & >::value, "~5.c"); 

static_assert(std::is_convertible< double, double const & >::value, "5*"); 
static_assert(!std::is_convertible< double, double & >::value, "9*"); 

Nếu bạn avail của gcc 4.7.2 (và có lẽ trước đó, tôi đã không kiểm tra) thì C++ 11 thư viện chuẩn giải quyết vấn đề:

std::is_explicitly_convertible<From,To> 

được định nghĩa trong <type_traits>

Tuy nhiên, sau đó bạn sẽ được availing của một error trong đó phiên bản của C++ 11 thư viện chuẩn. Mẫu đặc điểm này không nên có ở đó, bởi vì nó đã bị xóa khỏi Tiêu chuẩn theo N3047 (2010).

Nếu bạn ở trên gcc 4.8.1 (hoặc có thể 4.8; tôi chưa kiểm tra) thì đặc điểm này là không còn trong thư viện nữa, và nếu bạn muốn, bạn phải cuộn lại.

Nhưng nó sẽ chỉ là con người để kiểm tra việc định nghĩa trong gcc 4.7.2 của <type_traits> cho một sự khởi đầu, và làm điều đó cho thấy rằng người thực hiện GNU coi là đặc điểm là không có gì nhưng nghịch đảo của std::is_constructible<To,From>:

/// is_explicitly_convertible 
template<typename _From, typename _To> 
struct is_explicitly_convertible 
: public is_constructible<_To, _From> 
{ }; 

Người ta cũng có thể nghĩ rằng: Nhưng tất nhiên.

Vậy tại sao điều đó sẽ không xảy ra? N3047 giải thích:

Câu hỏi còn lại là, theo đó cách nào cũng bị ảnh hưởng is_explicitly_convertible đặc điểm cần được sửa chữa.Các lựa chọn cơ bản là:

  1. Fix is_explicitly_convertible bằng cách quay lại biểu static_cast hiện tại, không còn làm is_explicitly_convertible phụ thuộc vào is_constructible.
  2. Xóa is_explicitly_convertible khỏi tiêu chuẩn.

Lựa chọn đầu tiên được xem xét, nhưng hóa ra là có tồn tại những hiểu biết khá khác về những gì "chuyển đổi rõ ràng" thực sự có ý nghĩa. Trong khi một số người tin rằng static_cast thể hiện chính xác điều này, những người khác tin rằng số is_constructible cố định cũng sẽ mang lại ý nghĩa tốt hơn cho is_explicitly_convertible. Do đó bài viết này đề nghị xóa is_explicitly_convertible khỏi bản nháp đang hoạt động. Điều này sẽ không gây hại bây giờ, bởi vì không có gì phụ thuộc vào định nghĩa đặc biệt đó. Và nếu nó chỉ ra, rằng các đặc điểm vẫn sẽ hữu ích, nó có thể được thêm vào trong một phiên bản khác của tiêu chuẩn.

Không có lỗi được biết đến trong định nghĩa đó, nhưng đã có quan điểm trái ngược về việc liệu ý nghĩa của "một cách rõ ràng mui trần" mà nó hệ thống hóa là một trong những quyền: -

  • D1) From là một cách rõ ràng mui trần để To = df To là constructible từ From
  • D2) From là rõ ràng mui trần để To = df From có thể được tĩnh đúc để To

Tôi sẽ không tranh luận về vấn đề này (thậm chí tôi chưa từng biết sự khác biệt là gì), nhưng sẽ đề nghị bạn chỉ cần thanh toán tiền của bạn và lựa chọn.

Nếu bạn ưu tiên D1) thì bạn có thể chỉ cần định nghĩa đặc điểm từ gcc 4.7.2 <type_traits>, như trên.

Nếu bạn ưu tiên D2, bạn có thể đạo văn và điều chỉnh định nghĩa của std::is_convertible<From,To> từ gcc 4.8.1 <type_traits>. Định nghĩa này viện dẫn các chức năng phụ trợ bên trong mà bạn sẽ phải theo dõi.Sự thích nghi bạn muốn sẽ thay đổi thử nghiệm SFINAE cho From có thể được ngầm đúc để To đến một thử nghiệm cho From có thể static_cast để To; và điều đó có nghĩa thay thế:

template<typename _From1, typename _To1> 
    static decltype(__test_aux<_To1>(std::declval<_From1>()), __one()) 
__test(int); 

với:

template<typename _From1, typename _To1> 
    static decltype(__test_aux<_To1>(static_cast<_To1>(std::declval<_From1>())), __one()) 
__test(int); 

Một phiên bản cắt giảm của định nghĩa này (bỏ qua trường hợp như FromvoidTo là một chức năng hoặc mảng loại) mà sẽ làm cho các loại được kiểm tra trong số static_assert của bạn:

template<typename From, typename To> 
struct is_explicitly_convertible 
{ 
    template<typename T> 
    static void f(T); 

    template<typename F, typename T> 
    static constexpr auto test(int) -> 
    decltype(f(static_cast<T>(std::declval<F>())),true) { 
     return true; 
    } 

    template<typename F, typename T> 
    static constexpr auto test(...) -> bool { 
     return false; 
    } 

    static bool const value = test<From,To>(0); 
}; 

Định nghĩa D1 hoặc định nghĩa D2 cắt xuống duy trì tất cả 63 trong số static_assert giây của bạn. (tôi biên soạn với g ++ 4.7.2 và 4.8.1, -g;-O0;-Wall;-std=c++11, và cũng với các -std=gnu++1y mà bạn sử dụng)

giải pháp mới nhất của bạn cho thấy bạn đã thực hiện theo cách riêng của mình cho nhà trường D2, với một khác nhau phong cách thực hiện.

Trong hai, cho đến khi tôi tìm thấy một cái gì đó xảy ra với nó, tôi muốn định nghĩa D1, theo gcc 4.7.2, chỉ vì nó là nhiều đơn giản và đặc biệt là một dẫn xuất tầm thường của std::is_constructible.

+1

Cả hai D1) và D2) hoàn toàn tương đương với trường hợp thử nghiệm của tôi vào __gcc__ cuối cùng (mingw-builds 4.8.1 rev0) với tùy chọn '-std = gnu ++ 1y'. Vì vậy, cuối cùng, tôi chọn D1), vì nó không yêu cầu tôi viết bất cứ điều gì mới. – Orient

Các vấn đề liên quan