Выбор шаблона функции-члена и SFINAE

Я пытался понять, как C++ выбирает шаблоны. А именно, рассмотрим следующий пример кода:

template <typename R>
class Curious
{
public:
    template <typename T, typename std::enable_if<std::is_const<T>::value, int>::type = 33>
    void test1() {}

    template <typename T, typename std::enable_if<!std::is_const<T>::value, int>::type = 33>
    void test1() {}

    template <typename T, typename = typename std::enable_if<std::is_const<T>::value>::type>
    void test2() {}

    template <typename T, typename = typename std::enable_if<!std::is_const<T>::value>::type>
    void test2() {}

    template <typename std::enable_if<std::is_const<R>::value>::type * = nullptr>
    void test3() {}

    template <typename std::enable_if<!std::is_const<R>::value>::type * = nullptr>
    void test3() {}

    // works
    template <typename T = void>
    typename std::enable_if<std::is_const<R>::value, T>::type test4() {}

    template <typename T = void>
    typename std::enable_if<!std::is_const<R>::value, T>::type test4() {}

    // also works
    template <typename T = void, typename std::enable_if<std::is_const<R>::value, T>::type * = nullptr>
    void test5() {}

    template <typename T = void, typename std::enable_if<!std::is_const<R>::value, T>::type * = nullptr>
    void test5() {}
}; // Curious

первые две функции (test1) работают нормально (почему?):

Curious<int> curious;
curious.test1<int>();
curious.test1<const int>();

в то время как остальные из них вызывают ошибки компиляции. Относительно функции test2 компилятор утверждает, что я пытаюсь создать дубликат:

error C2535: 'void Curious::test2(void)': member function already defined or declared

здесь в документации сказано, что:

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

так что, похоже, это так. Однако я не вижу большой разницы с первыми двумя функциями, которые также имеют аргумент шаблона по умолчанию. Таким образом, у нас есть тип по умолчанию (test2 - не работает) против значения по умолчанию (test1-works). Есть какое-нибудь правило?

в случае test3:

error C2039: 'type': is not a member of 'std::enable_if'
Как и в первом случае, на этот раз шаблон функции-члена имеет параметр non-type по умолчанию, но он зависит от параметра шаблона класса. Теперь SFINAE не пропускает неправильный (также не уверен, почему).

в четвертом случае SFINAE разрешает шаблон по типу возврата. Но разве эти функции test4 не имеют идентичной подписи? Так как они отличаются только возвращением тип.

насколько я понимаю, в пятом случае добавление дополнительного параметра делает подпись test5 зависимой от параметра шаблона функции, поэтому SFINAE срабатывает и разрешение работает.

Я очень смущен тем, как C++ работает с этими шаблонами. Кто-нибудь может быть так добр, чтобы все прояснить?

1 ответов


  • при удалении значения по умолчанию для test1 у вас есть:

    template <typename T, typename std::enable_if<std::is_const<T>::value, int>::type>
    void test1();
    
    template <typename T, typename std::enable_if<!std::is_const<T>::value, int>::type>
    void test1();
    

    которые имеют явно разные подписи.

  • для test2:

    template <typename T, typename> void test2();
    
    template <typename T, typename> void test2();
    

    , которые являются идентичными подписями.

  • для test3 SFINAE не применяется, поскольку у вас есть жесткая ошибка как R исправлена в классе и ваш enable_if не зависит от параметра шаблона функция.

  • для test4 существует исключение о сигнатуре для функции шаблона, поскольку перегрузка может отличаться только типом возврата so

    int foo();
    char foo(); // Illegal.
    

    но

    template <typename T> int foo();
    template <typename T> char foo(); // legal, even it is not trivial to call
    

    кроме того, std::enable_if<!std::is_const<R>::value, T>::type зависит от параметра шаблона T так что все в порядке.

  • для test5, второй параметр шаблона зависит от первого параметра шаблона T, так это тоже нормально.