Подтвердить что ты не робот

Почему 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() { }

Почему это так? Что я могу сделать, чтобы иметь новый, более сжатый синтаксис в этом случае, если это возможно?

4b9b3361

Ответ 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() { }