Почему enable_if_t в аргументах шаблона жалуется на переопределение?
У меня есть следующий случай, который работает с помощью std::enable_if
:
template<typename T,
typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr>
void f() { }
template<typename T,
typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr>
void f() { }
Теперь я увидел в cppreference новый синтаксис, намного более чистый по моему мнению: typename = std::enable_if_t<std::is_same<int, T>::value>>
Я хотел портировать свой код:
template<typename T,
typename = std::enable_if_t<std::is_same<int, T>::value>>
void g() { }
template<typename T,
typename = std::enable_if_t<std::is_same<double, T>::value>>
void g() { }
Но теперь GCC (5.2) жалуется:
error: redefinition of 'template<class T, class> void g()'
void g() { }
Почему это так? Что я могу сделать, чтобы иметь новый, более сжатый синтаксис в этом случае, если это возможно?
Ответы
Ответ 1
Удалите некоторый код.
template<
class T,
class U/* = std::enable_if_t<std::is_same<int, T>::value>*/
>
void g() { }
template<
class T,
class U/* = std::enable_if_t<std::is_same<double, T>::value>*/
>
void g() { }
Вы были бы удивлены, если компилятор отклонил два вышеуказанных шаблона?
Они оба являются функциями шаблона "type" template<class,class>void()
. Дело в том, что аргумент второго типа имеет другое значение по умолчанию. Это было бы похоже на то, чтобы перегрузить две разные функции print(string, int)
с разными значениями по умолчанию int
.;)
В первом случае мы имеем:
template<
typename T,
typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr
>
void f() { }
template<
typename T,
typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr
>
void f() { }
здесь мы не можем удалить предложение enable_if
. Обновление до enable_if_t
:
template<
class T,
std::enable_if_t<std::is_same<int, T>::value>* = nullptr
>
void f() { }
template<
class T,
std::enable_if_t<std::is_same<double, T>::value>* = nullptr
>
void f() { }
Я также заменил использование typename
на class
. Я подозреваю, что ваше замешательство было связано с тем, что typename
имеет два значения - один как маркер для своего рода аргумента template
, а другой как disambiguator для зависимого типа.
Здесь второй аргумент - это указатель, тип которого зависит от первого. Компилятор не может определить, не конфликтуют ли эти два конфликта без первой замены в типе T
, и вы заметите, что они никогда не конфликтуют.
Ответ 2
enable_if_t<B>
- это просто псевдоним для typename enable_if<B>::type
. Подставим в g
, чтобы мы могли видеть реальную разницу между f
и g
:
template<typename T,
typename std::enable_if<std::is_same<int, T>::value>::type* = nullptr>
void f() { }
template<typename T,
typename std::enable_if<std::is_same<double, T>::value>::type* = nullptr>
void f() { }
template<typename T,
typename = typename std::enable_if<std::is_same<int, T>::value>::type>
void g() { }
template<typename T,
typename = typename std::enable_if<std::is_same<double, T>::value>::type>
void g() { }
В случае f
у нас есть два шаблона функций как с параметрами шаблона <typename, X*>
, где тип X
зависит от типа первого аргумента шаблона. В случае g
у нас есть два шаблона функций с параметрами шаблона <typename, typename>
, и это зависит только от аргумента шаблона по умолчанию, поэтому С++ считает, что они оба объявляют одну и ту же сущность.
Любой стиль можно использовать с псевдонимом enable_if_t
:
template<typename T,
std::enable_if_t<std::is_same<int, T>::value>* = nullptr>
void f() { }
template<typename T,
std::enable_if_t<std::is_same<double, T>::value>* = nullptr>
void f() { }
template<typename T,
typename = std::enable_if_t<std::is_same<int, T>::value>>
void g() { }
template<typename T,
typename = std::enable_if_t<std::is_same<double, T>::value>>
void g() { }
Ответ 3
Для возвращаемого типа функции вы ищете следующее:
template<typename T> std::enable_if_t< conditional, instantiation result > foo();
Пример:
#include <iostream>
// when T is "int", replace with 'void foo()'
template<typename T>
std::enable_if_t<std::is_same<int, T>::value, void> foo() {
std::cout << "foo int\n";
}
template<typename T>
std::enable_if_t<std::is_same<float, T>::value, void> foo() {
std::cout << "foo float\n";
}
int main() {
foo<int>();
foo<float>();
}
http://ideone.com/TB36gH
см. также
http://ideone.com/EfLkQy
Ответ 4
Вам не хватает типа ":: type".
template<typename T,
typename = std::enable_if_t<std::is_same<int, T>::value>::type>
void g() { }
template<typename T,
typename = std::enable_if_t<std::is_same<double, T>::value>::type>
void g() { }