проверьте, можно ли явно преобразовать тип

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

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

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

Ответ 1

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

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

Ответ 2

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

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 (и, возможно, раньше, я не проверял), тогда стандартная библиотека С++ 11 решает проблему:

std::is_explicitly_convertible<From,To>

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

Тем не менее, тогда вы воспользуетесь ошибкой в этой версии стандартной библиотеки С++ 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, возвращаясь к текущему выражению is_explicitly_convertible, больше не делая is_explicitly_convertible зависимым от is_constructible.
  2. Удалить is_explicitly_convertible из стандарта.

Первый вариант был рассмотрен, но оказалось, что существуют совершенно разные представления о том, что означает "явно конвертируемый". Хотя некоторые считают, что static_cast правильно выражает это, другие полагали, что фиксированное is_constructible обеспечит лучшее значение для is_explicitly_convertible а также. Поэтому в этом документе рекомендуется удалить 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 function или тип массива), которые будут выполняться для типов, протестированных в static_assert s:

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 s. (Я скомпилирован с g++ 4.7.2 и 4.8.1, -g;-O0;-Wall;-std=c++11, а также с -std=gnu++1y который вы использовали )

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

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