Различия в использовании std :: enable_if и универсальных ссылок - PullRequest
0 голосов
/ 05 октября 2018

Я пытаюсь понять универсальные ссылки и std::enable_if лучше, но я немного застрял в том, что здесь происходит в моем коде.

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

  1. template<typename T, std::enable_if<condition, T>::type* = nullptr> или что-то подобное.

  2. template<typename T> std::enable_if_t<condition, T> myfunc() {...} или что-то подобное.

Я понимаю, что происходит во втором, но я не понимаю, почему кто-то будет использовать первый.Чего это добивается, кроме добавления еще одного параметра в шаблон?Это вещь SFINAE?

Я также застрял на универсальных ссылках при использовании enable_if.Вот мой код и результаты, которые я получаю.Обратите внимание, что я использую код печати типа Говарда Хиннанта из " Возможно ли напечатать тип переменной в стандарте C ++? ", который я здесь опущу для краткости.

В любом случае,функция conditionless, кажется, работает нормально со всеми.

Я очень запутался в is_integral и decay, которые вы можете увидеть в начале main.Я получаю вывод:

true: unsigned long false: unsigned long false: unsigned long false: unsigned long

, и я понятия не имею, почему последние три являются ложными.

Тогда у меня есть проблемы (отмеченные 1 и 2 висточник ниже), где при использовании enable_if любым из двух упомянутых выше способов они отказываются компилироваться, принимая lvalue целочисленного типа или типа с плавающей запятой.

Заголовки и тип печатного кода опущены для краткости:

template<typename T>
void conditionless(T&& val) {
    std::cout << "conditionless(" << val << ")\n";
}

template<typename T, typename std::enable_if<std::is_integral_v<T>, T>::type* = nullptr>
void outputIntType(T&& val) {
    std::cout << "outputIntType(" << val << ")\n";
}

template<typename T>
typename std::enable_if_t<std::is_floating_point_v<T>>
outputFloatType(T&& val) {
    std::cout << "outputFloatType(" << val << ")\n";
}

int main() {

    size_t sz = 1;
    size_t &ref = sz;

    // All of these report as having type "unsigned long", but for some reason, the first reports true for is_integral, and
    // the other three report false.
    std::cout << std::boolalpha << std::is_integral_v<decltype(sz)> << ": " << type_name<decltype(sz)>() << '\n';
    std::cout << std::boolalpha << std::is_integral_v<std::decay<decltype(sz)>> << ": " << type_name<std::decay<decltype(sz)>::type>() << '\n';
    std::cout << std::boolalpha << std::is_integral_v<decltype(ref)> << ": " << type_name<decltype(sz)>() << '\n';
    std::cout << std::boolalpha << std::is_integral_v<std::decay<decltype(ref)>> << ": " << type_name<std::decay<decltype(ref)>::type>() <<'\n';

    //  This works fine.
    conditionless(sz);
    conditionless(2UL);
    conditionless(2L + 1);

    // ******* 1 *******
    // This fails and claims no matching function call to outputIntType(size_t&)
    // template argument deduction / substitution failed:
    // error: no type named 'type' in 'struct std::enable_if<false, long unisgned int&>'
    // I'm particularly confused about why the is_integral evaluates to false.
    //outputIntType(sz); 

    // These work fine.
    outputIntType(2UL);
    outputIntType(2L + 1);

    double pi = 3.1415926535;

    // These work fine.
    conditionless(pi);
    conditionless(2 * pi);
    conditionless(0.00000001);

    // ******* 2 *******
    // This fails as well:
    // main.cpp: In function 'int main()':
    // error: no matching function for call to 'outputFloatType(double&)'
    // note: candidate: 'template<class T> std::enable_if_t<is_floating_point_v<T> > outputFloatType(T&&)'
    // template argument deduction/substitution failed:
    // outputFloatType(pi);

    // These work fine.
    outputFloatType(2 * pi);
    outputFloatType(0.00000001);
}

Любое понимание того, что кто-либо может дать мне представление о двух разных вариантах использования enable_if и почему мой код с enable_if отказывается принимать lvalues, будет очень признательно.

1 Ответ

0 голосов
/ 05 октября 2018

Я пытаюсь понять универсальные ссылки

Использование этого термина не рекомендуется.Официальный термин «пересылка ссылок» .


Я понимаю, что происходит во втором, но я не понимаю, почему кто-то будет использовать первый.Чего это добивается, кроме добавления еще одного параметра в шаблон?Это вещь SFINAE?

Все, что делает enable_if_t<B, T>, оценивается как T, если B == true, в противном случае он выдает неверный код .Неверный код, созданный во время подстановки, не приводит к ошибке компиляции (SFINAE).

Неважно, где появляется enable_if_t, если на него влияет шаг замещения (например, может быть в типе возврата, списке параметров, списке параметров шаблона, ...).


и я понятия не имею, почему последние три являются ложными.

Вы забыли получить доступ к ::type в своем преобразовании std::decay.Вы сравниваете саму черту, а не ее результат.


они отказываются компилировать, принимая lvalue целочисленного типа или типа с плавающей запятой.

Существуетспециальное правило, касающееся удержания пересылочных ссылок в Стандарте.Если задан параметр переадресации T&&, T будет выведен как значение l * value , если функция вызывается с lvalue .

Вы должны принять это во внимание в своих чертах:

typename std::enable_if_t<std::is_floating_point_v<std::remove_reference_t<T>>>
...