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

В ссылка на язык std::enable_if на cppreference включена следующая заметка

Примечания

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

В функциях шаблона в примере ниже мне кажется, что эта ситуация происходит. I.e, две функции шаблона 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 T& obj)
{
  return 2*obj.getInt();
}

template< class T,
          typename std::enable_if<std::is_base_of<BaseB, T>::value>::type* = nullptr >
int onlyForDerivedObjects(const T& 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, но второй шаблон параметр отличается в каждом случае.

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