Bikineev Bikineev - 2 months ago 14
C++ Question

C++ partial ordering for a function with default argument

Consider the following code:

template <class...>
using void_t = void;

template <class T>
void bar(T){}

template <class T>
void bar(T, void_t<decltype(std::declval<T>().foo())>* = 0) {}

struct A { void foo(); } a;
bar(a); // gives a compiler error on ambiguous call


So the question is, why are these overloads ambiguous? Why is the second overload not considered to be more restrictive, more specialized than second one by the compiler?

Answer

one of the usual ways to do this is create a disjunction of equally-specialised templates.

#include <utility>
#include <iostream>

template<class T>
struct has_foo_impl
{
  template<class U> static auto test(U* p) -> decltype(p->foo(), void(), std::true_type()); 
  static auto test(...) -> decltype(std::false_type()); 

  using type = decltype(test((T*)nullptr));
};

template<class T> using has_foo = typename has_foo_impl<T>::type;

template <class...>
using void_t = void;

template <class T, std::enable_if_t<not has_foo<T>::value>* = nullptr>
void bar(T){
  std::cout << "not me\n";
}

template <class T, std::enable_if_t<has_foo<T>::value>* = nullptr>
void bar(T) {
  std::cout << "me\n";
}

struct A { void foo(); } a;

int main()
{
    bar(a); // "me"
}