2013-06-03 10 views
11

Как определить (в духе <type_traits>) один или несколько типов, явно конвертируемых в другой тип? Например, я хочу, чтобы проверить наличие F::explicit operator double const &() const; для некоторого class/structF, но, в то же время, F не должен был явно конвертируемым в float или long double (что-то вроде pred< double const & >::value && !pred<float>::value && !pred< long double >::value).проверить, может ли тип быть явно преобразован

Обратите внимание, что std::is_convertible< From, To >::value проверки «если Из могут быть преобразованы в Чтобы используя неявное преобразование». Но я хочу определить, есть ли явный конвертер .

И, если это возможно, "как определить тип Из конвертируется в именно ссылку типа To?"?

+0

Возможно, я не понимаю вопрос, но вы можете сделать 'dynamic_cast (FROM & val)', он будет бросать 'std :: bad_cast', если преобразование не подходит. –

+0

@ H2CO3 Предположим, что вопрос о времени компиляции. – Orient

+2

Как вы хотите исследовать явный оператор преобразования, ваша задача, похоже, сводится к [определению того, имеет ли класс T функцию-член с указанной сигнатурой] (http://stackoverflow.com/q/87372/1362568). Есть много ответов там, в том числе один из моих. –

ответ

11

Вы должны определить свои собственные:

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

Ваш тестовый набор находится на ссылке внеплощадочных и варьировалась с момента первоначального поста, поэтому я буду копировать дословно тест-набор я говорю здесь:

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*"); 

Если вы воспользоваться GCC 4.7.2 (и, возможно, раньше, я не проверял), то C++ 11 Стандартная библиотека решает эту проблему:

std::is_explicitly_convertible<From,To> 

определяется в <type_traits>

Однако, вы бы тогда пользующаяся из error в этой версии C++ 11 стандартной библиотеки в. Этот шаблон шаблона не должен был быть там, потому что он был удален из Стандарта согласно N3047 (2010).

Если вы находитесь на gcc 4.8.1 (или, возможно, 4.8; я не проверял), то этот признак больше не находится в библиотеке, и если вы хотите его, вы должны снова свернуть свой.

Но было бы только человек, чтобы проверить определение в GCC 4.7.2-х <type_traits> для начала, и делать это показывает, что реализатор GNU считается чертой не должно быть ничего, кроме обратной std::is_constructible<To,From>:

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

Можно подумать: Но, конечно.

Так почему же это не должно продолжаться? N3047 объясняет:

Оставшийся вопрос в том, каким образом также следует отредактировать свойство is_explicitly_convertible.Основные варианты:

  1. Fix is_explicitly_convertible, вернувшись к текущему выражению static_cast, больше не делает is_explicitly_convertible зависимой от is_constructible.
  2. Удалить is_explicitly_convertible со стандарта.

Первый выбор был рассмотрен, но оказалось, что существует совершенно иное понимание того, что означает «явно конвертируемый». Хотя некоторые считают, что static_cast правильно выражают это, другие полагали, что фиксированный is_constructible обеспечит лучшее значение для is_explicitly_convertible. Поэтому в настоящем документе рекомендуется удалить is_explicitly_convertible из рабочего чертежа. Это не должно наносить вреда сейчас, потому что ничто не зависит от этого специального определения. И если окажется, что этот признак будет полезен, его можно было бы добавить в другой редакции стандарта.

Там не было известно ошибка в этом определении, но были противоположные мнения относительно того, является ли значение «явно конвертируемый», что он шифрует является правильным: -

  • D1) From İŞ явно конвертируемый в To = DF To построим от From
  • D2) From явно конвертируемая в To = DF From может быть статический отливают, чтобы To

Я не буду спорить с этим споры (я даже не работал, что разница есть, и я), но будет предположить, что вы просто заплатить деньги и взять ваш выбор.

Если вы пользуетесь D1), вы можете просто взять определение характеристики от gcc 4.7.2 <type_traits>, как указано выше.

Если вы пользуетесь D2, тогда вы можете плагиатом и адаптировать определение std::is_convertible<From,To> от gcc 4.8.1 <type_traits>. Это определение вызывает внутренние вспомогательные функции, которые вам нужно будет отслеживать.Адаптация вы хотели бы изменить тест SFINAE для From может быть неявно приведен к To к испытанию для From может быть static_cast к To; и это означало бы замена:

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

с:

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

версия урезанная этого определения (без учета таких случаев, как From быть void и To является функцией или массив типа), который сделало бы для типов, которые были протестированы в ваших static_assert с:

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); 
}; 

Либо определение D1, либо определение D2 для вырезания поддерживают все 63 вашего static_assert с. (я скомпилирован с г ++ 4.7.2 и 4.8.1, -g;-O0;-Wall;-std=c++11, а также с -std=gnu++1y что вы использовали)

Ваше последнее решение показывает, что вы сделали свой собственный путь в школу D2, с другой стиль реализации.

Из двух, пока я не найду что-то не так с ним, я бы предпочел определение D1, согласно НКУ 4.7.2, просто потому, что гораздо самый простой и, в частности, является тривиальным производной std::is_constructible.

+1

Оба подхода D1) и D2) полностью эквивалентны в моем тестовом примере на последнем __gcc__ (mingw-builds 4.8.1 rev0) с опцией '-std = gnu ++ 1y'. Итак, наконец, я выбираю D1), так как он не требует, чтобы я писал что-то новое. – Orient

Смежные вопросы