std :: enable_if, основанный на том, что std :: is_convertible неправильно выводит шаблон

У меня такой код:

#include <iostream>
#include <type_traits>

template <typename T, typename std::enable_if
                                 <std::is_convertible<int, T>::value, T>::type>
void func(T a)
{
    std::cout << a << std::endl;
}

template <typename T, typename std::enable_if
                                 <!std::is_convertible<int, T>::value, T>::type>
void func(T a)
{
    a.print();
}

class Test
{
public:
    void print()
    {
        std::cout << "Test" << std::endl;
    }
};    

int main()
{
    func(3);
    func("Test");
    return 0;
}

С помощью этого кода я ожидал, что первый вызов func распечатает 3 (поскольку int действительно может быть преобразован в int, должна быть вызвана первая специализация), а второй вызов func для распечатки Test (Test() не преобразовывается в int, поэтому вторую специализацию следует назвать). Однако вместо этого я получаю ошибку компилятора:

prog.cpp: В функции ‘int main ()’:

prog.cpp: 27: 8: error: нет соответствующей функции для вызова «func (int)»

prog.cpp: 5: 6: примечание: кандидат: шаблон [класс T, typename std :: enable_if [std :: is_convertible [int, T ›:: value, T› :: type ›void func (T)

prog.cpp: 5: 6: примечание: ошибка вывода / подстановки аргумента шаблона:

prog.cpp: 27: 8: примечание: не удалось определить параметр шаблона ‘[анонимный› ’

Если, однако, я заменю шаблонные функции на be (оставив все остальное точно так же):

template <typename T, typename std::enable_if
                                 <std::is_convertible<int, T>::value, T>::type* =
                                  nullptr>
void func(T a)
{
    std::cout << a << std::endl;
}

template <typename T, typename std::enable_if
                                 <!std::is_convertible<int, T>::value, T>::type* =
                                  nullptr>
void func(T a)
{
    a.print();
}

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


person R_Kapp    schedule 07.07.2017    source источник
comment
дубликат Как работает std :: enable_if?   -  person underscore_d    schedule 07.07.2017


Ответы (1)


template<typename T, typename std::enable_if<std::is_convertible<int, T>::value, T>::type>

если бы мы удалили шум, стало бы

template<typename T, typename Something<T>::type>

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

В первом случае второй параметр не является типом, поэтому вызов функции func(3) не соответствует шаблону, ожидающему func<int, some_int>(3).

person Passer By    schedule 07.07.2017
comment
@downvoter любезно объясните, я заинтригован, почему это воспринимается как достойное отрицательного голоса - person Passer By; 07.07.2017
comment
Вы не ответили на вопрос. Я отметил в самом вопросе, что предоставление параметра по умолчанию nullptr решает проблему - почему требуется параметр по умолчанию? - person R_Kapp; 07.07.2017
comment
@R_Kapp Я ответил первыми тремя предложениями. Все, что ниже, добавлено, потому что я предполагал, что вы не знакомы с SFINAE. - person Passer By; 07.07.2017
comment
Параметр шаблона приведет к синтаксической ошибке с правильным аргументом по умолчанию или без него (если логическое значение в std::enable_if равно false)? Как добавление аргумента по умолчанию меняет ситуацию? - person R_Kapp; 07.07.2017
comment
Т.е. вместо template <Test, ???* = nullptr> было бы template <Test, ???>? - person R_Kapp; 07.07.2017
comment
@R_Kapp Да, но тогда ваш шаблон не подходит для вызова функции - person Passer By; 07.07.2017
comment
Почему std::enable_if<std::is_convertible<int, T>, T>::type, который должен преобразовать в std::enable_if<true, int>::type для T = int, а затем в int, не подходит для вызова функции для func(3);? - person R_Kapp; 07.07.2017
comment
@R_Kapp Поскольку второй параметр не является типом, вам нужно будет указать ему значение типа func<42>(3);, но поскольку это второй параметр, вам нужно будет написать func<int, 42>(3) - person Passer By; 07.07.2017
comment
Для меня этот последний комментарий является ответом в том, что я не понял с синтаксисом. В этом есть смысл - person R_Kapp; 07.07.2017
comment
@R_Kapp Да, я совершенно не понял - person Passer By; 07.07.2017