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

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


person Jean-Michaël Celerier    schedule 19.07.2015    source источник
comment
Ваш второй код использует аргументы шаблона по умолчанию. Они не являются частью сигнатуры функции, поэтому вы объявляете два шаблона функций с одной и той же сигнатурой = переопределением. Соответствующее использование enable_if_t буквально std::enable_if_t<std::is_same<int, T>::value>* = nullptr.   -  person dyp    schedule 19.07.2015
comment
Ну, вы не совсем точно переписали исходный код. Вы забыли * = nullptr.   -  person Kerrek SB    schedule 19.07.2015
comment
Вы можете добавить фиктивный параметр шаблона в одно из объявлений, например , typename = void после enable_if   -  person Piotr Skotnicki    schedule 19.07.2015
comment
@dyp: я думаю, вы можете оставить свой комментарий в качестве ответа! @KerrekSB: нет, в этом суть enable_if_t @PiotrS. : Ok   -  person Jean-Michaël Celerier    schedule 19.07.2015
comment
Вероятно, дубликат: stackoverflow.com/q/15427667 или stackoverflow.com/q/28674543 или stackoverflow.com/q/8743159   -  person dyp    schedule 19.07.2015
comment
Я пошел по пути typename std::enable_if_t<std::is_same<int, T>::value>* = nullptr. Получилось 4 символа, да ладно :p   -  person Jean-Michaël Celerier    schedule 19.07.2015
comment
@ Jean-MichaëlCelerier Вам не нужно typename перед std::enable_if_t. Получено больше персонажей.   -  person bogdan    schedule 19.07.2015
comment
Еще больше символов можно получить с помощью std::enable_if_t<std::is_same<int, T>::value, int> = 0 или std::enable_if_t<std::is_same_v<int, T>, int> = 0.   -  person Oktalist    schedule 19.07.2015
comment
@Oktalist Только что попробовал, но мой компилятор (gcc 4.9) жалуется на *=0, а не на *=nullptr.   -  person Jean-Michaël Celerier    schedule 21.07.2015
comment
Вот почему я не сказал *=0. Я указал int в качестве второго аргумента для enable_if_t вместо стандартного void, поэтому 0 является целочисленным литералом, а не литералом нулевого указателя.   -  person Oktalist    schedule 21.07.2015
comment
Я должен научиться читать.   -  person Jean-Michaël Celerier    schedule 21.07.2015


Ответы (4)


Удалим немного кода.

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() { }

Вы бы удивились, если бы компилятор отклонил два вышеуказанных шаблона?

Обе они являются шаблонными функциями "типа" template<class,class>void(). Тот факт, что аргумент 2-го типа имеет другое значение по умолчанию, не имеет значения. Это было бы похоже на ожидание перегрузки двух разных функций 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, int>* = nullptr
>
void f() { }

template<
  class T,
  std::enable_if_t<std::is_same<double, T>::value, int>* = nullptr
>
void f() { }

Я также заменил использование typename на class. Я подозреваю, что вы запутались, потому что typename имеет два значения: одно как маркер для своего рода аргумента template, а другое как средство устранения неоднозначности для зависимого типа.

Здесь второй аргумент — это указатель, тип которого зависит от первого. Компилятор не может определить, конфликтуют ли эти два типа, без предварительной замены типа T, и вы заметите, что на самом деле они никогда не будут конфликтовать.

person Yakk - Adam Nevraumont    schedule 20.07.2015

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>, и зависит только аргумент шаблона по умолчанию, поэтому C++ считает, что они оба объявляют одну и ту же сущность.

Любой стиль можно использовать с псевдонимом 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() { }
person Oktalist    schedule 19.07.2015
comment
Если бы я мог принять несколько ответов, я бы принял и ваш, потому что он одинаково действителен! Спасибо ! - person Jean-Michaël Celerier; 21.07.2015
comment
Нет проблем, объяснение Якка немного лучше. - person Oktalist; 21.07.2015

Для возвращаемого типа функции вы ищете следующее:

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

person kfsone    schedule 19.07.2015

Вам не хватает "::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() { }
person themoondothshine    schedule 19.07.2015
comment
std::enable_if_t — это шаблон псевдонима вложенного ::type определения std::enable_if. - person Piotr Skotnicki; 19.07.2015