Аргумент шаблона по умолчанию при использовании std::enable_if в качестве templ . param.: почему OK с двумя функциями шаблона, которые отличаются только параметром enable_if?

#c #sfinae

#c #шаблоны #c 11 #включить-если

Вопрос:

В языковом справочнике std::enable_if at cppreference включено следующее примечание

Примечания

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

Мне кажется, что в функциях шаблона в примере ниже возникает такая ситуация. Т.Е. Две функции шаблона onlyForDerivedObjects(...) кажутся (мне) различающимися только их аргументами шаблона по умолчанию. Я понимаю, что мне здесь чего-то не хватает, и, надеюсь, кто-нибудь сможет мне это объяснить или указать мне направление, где я мог бы найти прозрение для себя.

  • Вопрос: W.r.t. приведенная выше цитата, почему приведенный ниже пример компилируется и выполняется нормально: я неправильно классифицирую typename std::enable_if ... часть в приведенных ниже функциях шаблона, когда я считаю, что это приводит к ситуации с двумя функциями шаблона, которые отличаются только своим аргументом шаблона по умолчанию?

Пример

Базовые и производные классы:

 class BaseA
{
public:
  int getInt() const { return 21; };
};

class DerivedA : public BaseA {};

class BaseB
{
public:
  int getAnotherInt() const { return 33; };
};

class DerivedB : public BaseB {};
  

со следующими функциями шаблона

 /* template functions that, seemingly, only differ in their
   default template arguments? */
template< class T,
          typename std::enable_if<std::is_base_of<BaseA, T>::value>::type* = nullptr >
int onlyForDerivedObjects(const Tamp; obj)
{
  return 2*obj.getInt();
}

template< class T,
          typename std::enable_if<std::is_base_of<BaseB, T>::value>::type* = nullptr >
int onlyForDerivedObjects(const Tamp; obj)
{
  return 3*obj.getAnotherInt();
}
  

компилируется и работает нормально ( g -Wall -std=c 11 ... , g 4.9.3 )

 #include <iostream>
#include <type_traits>

/* ... classes and template functions as above */

/* template argument deduction seems to work fine */
int main()
{
  DerivedA* objA = new DerivedA();
  DerivedB* objB = new DerivedB();

  std::cout << onlyForDerivedObjects(*objA) << std::endl; // 42
  std::cout << onlyForDerivedObjects(*objB) << std::endl; // 99

  return 0;
}
  

Ответ №1:

Примечания

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

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

В обоих случаях аргумент шаблона по умолчанию nullptr равен , но второй параметр шаблона отличается в каждом случае.

Комментарии:

1. можете ли вы уточнить, чем они отличаются? насколько я понимаю: оба типа разрешаются как void * или nothing . И для обоих случаев значением по умолчанию является nullptr, так что для меня они точно такие же.

2. @DawidPilarski подпись включает в себя весь enable_if тип, а не только то, что он разрешает после вычета. Эти типы разные, потому что они зависят от разных выражений (одно используется is_base_of<BaseA, T> и одно используется is_base_of<BaseB, T> ).

Ответ №2:

Распространенная ошибка:

 template <typename T, typename = std::enable_if_t<cond>>
void foo()

template <typename T, typename = std::enable_if_t<!cond>>
void foo()
  

которые оба объявляют

 template <typename, typename>
void foo();
  

Комментарии:

1. struct T { enum { int_t, float_t } type; template <typename Integer, std::enable_if_t<std::is_integral<Integer>::value, bool> = true > T(Integer) : type(int_t) {} template <typename Floating, std::enable_if_t<std::is_floating_point<Floating>::value, bool> = true > T(Floating) : type(float_t) {} // OK }; @Jarod42 основываясь на вашем ответе выше, не могли бы вы помочь уточнить, почему работает приведенный выше пример ссылки на CPP?

2. У вас есть 2 разных конструктора: template <typename U, std::enable_if_t<std::is_integral<U>>, bool> T(U); и template <typename U, std::enable_if_t<std::is_floating_point<U>>> T(U);