Barry
Barry

Reputation: 302718

Why can't I template overload?

Why is it that this compiles:

class Testable {
public:
    template <bool flag>
    typename std::enable_if<flag>::type
    void foo() { cout << "Yay" << endl; }

    template <bool flag>
    typename std::enable_if<!flag>::type
    void foo() { cout << "Nay" << endl; }
};

But not if I define both foos using default types like so:

    template <bool flag, typename = typename std::enable_if<flag>::type>
    void foo() { cout << "Yay" << endl; } // (A)

    template <bool flag, typename = typename std::enable_if<!flag>::type>
    void foo() { cout << "Nay" << endl; } // (B)

I get this error (first line pointing to the definition of (B), second one pointing to (A)):

error: 'template<bool flag, class> void Testable::foo()' cannot be overloaded
error: with 'template<bool flag, class>> void Testable::foo()'

Upvotes: 11

Views: 2803

Answers (2)

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275260

As noted, two functions cannot have the same signature.

However, I have a different fix.

template<std::size_t>
struct secret_enum { enum class type {}; };
template<bool b, std::size_t n=0>
using EnableIf = typename std::enable_if< b, typename secret_enum<n>::type >::type;

class Testable {
public:
  template <bool flag, EnableIf<flag, 0>...>
  void foo() { cout << "Yay" << endl; } // (A)

  template <bool flag, EnableIf<!flag, 1>...>
  void foo() { cout << "Nay" << endl; } // (B)
};

where you enumerate your overloads with 0, 1, etc, which generates a possible type, which you then ... to say "0 or more of these", and the types generated are basically impossible to generate enum instances.

Sadly, this doesn't work in clang 3.2. It does in gcc 4.8.

Upvotes: 1

Andy Prowl
Andy Prowl

Reputation: 126412

The compiler complains because the two function templates have the same signature. Paragraph 1.3.18 of the C++11 Standard specifies what the signature of a function template is defined by:

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

As you can see, default template arguments are not part of the signature.

You could work around this problem by changing your definition of Testable as follows:

class Testable {
public:
    template <bool flag, typename std::enable_if<flag>::type* = nullptr>
    void foo() { cout << "Yay" << endl; } // (A)

    template <bool flag, typename std::enable_if<!flag>::type* = nullptr>
    void foo() { cout << "Nay" << endl; } // (B)
};

Upvotes: 11

Related Questions