6

The following code fails to compile because of error: redefinition of ‘template<class Integer, class> void func(Integer)’

#include <iostream>
#include <type_traits>

template<typename Float, typename = typename 
std::enable_if<std::is_floating_point<Float>::value>::type>
void func(Float floatVal)
{
    std::cerr << "float: " << floatVal << "\n";
}

template<typename Integer, typename = typename 
std::enable_if<std::is_integral<Integer>::value>::type>
void func(Integer integer)
{
    std::cerr << "integral: " << integer << "\n";
}

int main()
{
    func(32.4246);
    func(144532);
}

But the two functions will clearly have different signatures on template instantiation. So why is can't this compile?

Please note: I do know how to fix this: just adding another dummy template parameter to one of the functions, e.g. typename=void, will work, like here

template<typename Integer, typename dummy=void, typename = typename 
std::enable_if<std::is_integral<Integer>::value>::type>
void func(Integer integer){}

But the question is why do I have to do this?

Ruslan
  • 18,162
  • 8
  • 67
  • 136
  • the problem is that you're defaulting the template type for the second template arg. – AndyG Jul 28 '15 at 12:56
  • You can't give another template default parameter to other declarations in the same scope. – edmz Jul 28 '15 at 12:58

3 Answers3

5

N4527 §1.3.19 [defns.signature.templ]

signature

<function template> name, parameter type list (8.3.5), enclosing namespace (if any), return type, and template parameter list

The default template argument is not part of the signature of a function template.

cpplearner
  • 13,776
  • 2
  • 47
  • 72
4

You can change std::enable_if<...>::type as the return type of the function. As far as I know you cannot pass it to the type of another template parameter.

#include <iostream>
#include <type_traits>

template<typename Float> 
typename std::enable_if<std::is_floating_point<Float>::value>::type
func(Float floatVal)
{
    std::cerr << "float: " << floatVal << "\n";
}

template<typename Integer>
typename std::enable_if<std::is_integral<Integer>::value>::type
func(Integer integer)
{
    std::cerr << "integral: " << integer << "\n";
}

int main()
{
    func(32.4246);
    func(144532);
}

Live Example

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • This won't work if the functions are constructors (my example was just simplified because the problem reproduces with free functions). Also, this doesn't answer _the_ question: "why?". – Ruslan Jul 28 '15 at 13:05
2

Alternatively to overloading on return type like NathanOliver did, you can be a little more complicated in the template types:

template<typename Float, typename std::enable_if<std::is_floating_point<Float>::value>::type* = nullptr>
void func(Float floatVal)
{
    std::cerr << "float: " << floatVal << "\n";
}

template<typename Integer, typename std::enable_if<!std::is_floating_point<Integer>::value && std::is_integral<Integer>::value>::type* = nullptr>
void func(Integer integer)
{
    std::cerr << "integral: " << integer << "\n";
}

Live Demo
Notice that the second enable_if for Integer explicitly negates the enable_if condition for Float

The benefit of this approach is that your functions still return void

And test it:

int main()
{
    func(32.4246);
    func(144532);
}

Output:

float: 32.4246
integral: 144532
Community
  • 1
  • 1
AndyG
  • 39,700
  • 8
  • 109
  • 143