Почему значение по умолчанию необходимо для `std :: enable_if`? - PullRequest
2 голосов
/ 19 мая 2019

Почему я должен использовать значение по умолчанию (::type = 0) в этом std::enable_if использовании?

Я вижу примеры, где это работает без него.например https://foonathan.net/blog/2015/11/30/overload-resolution-4.html

#include<iostream>
#include<type_traits>

template <typename T,
          typename std::enable_if<std::is_integral<T>::value, T>::type = 0>
void do_stuff(T t) {
    std::cout << "do_stuff integral\n";
}

template <typename T,
          typename std::enable_if<std::is_class<T>::value, T>::type = 0>
void do_stuff(T t) {
    std::cout << "do_stuff class\n";
}

int main()
{
    do_stuff(32);
    return 0;
}

Я получил сообщение об ошибке:

temp.cpp:6:6: note:   template argument deduction/substitution failed:
temp.cpp:18:13: note:   couldn't deduce template parameter ‘<anonymous>’

Это должно быть выведено на

template <template T, int>
void do_stuff(T t)

, который является допустимым кодом.Что я делаю не так?( gcc версия 7.4.0 )

Ответы [ 2 ]

1 голос
/ 19 мая 2019

Компилятор четко объяснил вам, в чем проблема: в объявлении шаблона вы указали дополнительный тип-тип шаблона, который не может быть выведен.Как вы ожидаете, что компилятор выведет правильное значение для этого нетипового параметра?От чего?

Именно поэтому вышеупомянутая техника для использования std::enable_if требует аргумента по умолчанию.Это фиктивный параметр, поэтому значение аргумента по умолчанию не имеет значения (0 - это естественный выбор).

Вы можете сократить ваш пример до простого

template <typename T, T x> 
void foo(T t) {}

int main()
{
  foo(42);
}

Производство

error: no matching function for call to 'foo(int)'
note:   template argument deduction/substitution failed:
note:   couldn't deduce template parameter 'x'

Компилятор может определить, что такое T (T == int), но компилятор не может определить аргумент для x.

Ваш код точно такой же, за исключением того, что ваш второй параметр шаблона остался безымянным (не нужно давать имя фиктивному параметру).


Судя по вашим комментариям, вы, кажется, смущены присутствием ключевого слова typenameв объявлении второго параметра в вашем коде, который заставляет вас поверить, что второй параметр также является параметром type .Последнее не соответствует действительности.

Обратите внимание, что в объявлении второго параметра ключевое слово typename используется в совершенно другой роли .Это ключевое слово просто устраняет неоднозначность семантики

std::enable_if<std::is_class<T>::value, T>::type

. Оно сообщает компилятору, что вложенное имя type фактически представляет собой имя типа , а не чего-то другого.(Вы можете прочитать об этом использовании typename здесь: Зачем нам здесь имя типа? и Где и почему я должен поставить ключевые слова "template" и "typename"? )

Использование typename не превращает второй параметр вашего шаблона в параметр type .Вторым параметром вашего шаблона по-прежнему является не тип параметр.

Вот еще один упрощенный пример, который иллюстрирует, что происходит в вашем коде

struct S { typedef int nested_type; };

template <typename T, typename T::nested_type x>
void bar(T t)
{}

int main()
{
  S s;
  bar<S, 42>(s);
}

Обратите внимание, что даже еслиобъявление второго параметра начинается с typename, оно все еще объявляет параметр не-типа.

1 голос
/ 19 мая 2019

Я не могу воспроизвести вашу ошибку;в любом случае я получаю сообщение об ошибке do_stuff() с классом.Например,

do_stuff(std::string{"abc"})

Это потому, что do_stuff() становится do_stuff<std::string, std::string = 0>(), а значение шаблона не может иметь тип std::string (и не может иметь значение по умолчанию ноль).

Предложение: переписать ваши функции, указав int как тип для значения во второй позиции

template <typename T, // .....................................VVV  int, not T
          typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void do_stuff(T t) {
    std::cout << "do_stuff integral\n";
}

template <typename T, // ..................................VVV  int, not T
          typename std::enable_if<std::is_class<T>::value, int>::type = 0>
void do_stuff(T t) {
    std::cout << "do_stuff class\n";
}

Таким образом, вызывая do_stuff(std::string{"abc"}), вы включаете do_stuff<std::string, int = 0>(), что приемлемо.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...