Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default template argument when using std::enable_if as templ. param.: why OK with two template functions that differ only in the enable_if parameter?

In the language reference of std::enable_if at cppreference the following note is included

Notes

A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.

In the template functions in example below, it seems to me that this situation occurs. I.e., the two template functions onlyForDerivedObjects(...) seem (to me) to differ only by their default template arguments. I realize I am missing something here, and hopefully someone can explain this to me, or point me in the direction to where I might find an epiphany for myself.

  • Question: W.r.t. the quote above, why do the example below compile and run fine: do I misclassify the typename std::enable_if ... part in the template functions below when I consider it to yield a situation with two template functions which differ only in their default template argument?

Example

  • Live demo

Base and derived classes:

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

class DerivedA : public BaseA {};

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

class DerivedB : public BaseB {};

with the following template functions

/* 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();
}

compiles and runs fine (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;
}
like image 385
dfrib Avatar asked Jul 11 '16 10:07

dfrib


People also ask

What is the purpose of std :: Enable_if?

In C++ metaprogramming, std::enable_if is an important function to enable certain types for template specialization via some predicates known at the compile time. Using types that are not enabled by std::enable_if for template specialization will result in compile-time error.

Can template have default parameters?

Template parameters may have default arguments. The set of default template arguments accumulates over all declarations of a given template.

Can a template be a template parameter?

A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)

What can the template parameter in C++ template definition be?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.


2 Answers

Notes

A common mistake is to declare two function templates that differ only in their default template arguments. This is illegal because default template arguments are not part of function template's signature, and declaring two different function templates with the same signature is illegal.

Your functions don't differ only in their default template arguments, they differ in their template parameters, so have different signatures.

In both cases the default template argument is nullptr, but the second template parameter is different in each case.

like image 75
Jonathan Wakely Avatar answered Oct 22 '22 09:10

Jonathan Wakely


The common mistake is:

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

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

which both declare

template <typename, typename>
void foo();
like image 9
Jarod42 Avatar answered Oct 22 '22 08:10

Jarod42