Search code examples
c++11templatessfinaetemplate-specializationtemplate-argument-deduction

Why the overload resolution doesn't select the more specialised for a defaulted single template parameter


With 2 template parameters, the second one as defaulted, I get what I expect when the specialisation results in explicitly supplying what was otherwise the default type to the second parameter. In this case the specialisation is well formed and since that's more specialised, it gets chosen.

When I repeat that with just one template argument, also defaulted, the specialisation seems to be still well formed, explicitly supplying the type which is otherwise default in the base template. However in this case it seems that this is ignored and not considered more specialised. The base version is always chosen.

Can someone please explain why this happens ?

#include <iostream>
#include <utility>
#include <type_traits>

struct F1 { int operator()(); };
struct F2 {};

template<typename T, typename = int> struct A { constexpr static bool function_call_operator{false}; };
template<typename T> struct A<T, decltype(std::declval<T>()())> { constexpr static bool function_call_operator{true}; };

template<typename T = int> struct B { constexpr static bool function_call_operator{false}; };
template<typename T> struct B<decltype(std::declval<T>()())> { constexpr static bool function_call_operator{true}; };

void f0() {}

int main() {
    std::cout << std::boolalpha;
    std::cout << A<F1>::function_call_operator << std::endl; // true; OK
    std::cout << A<F2>::function_call_operator << std::endl; // false; OK

    std::cout << B<F1>::function_call_operator << std::endl; // false; why ?
    std::cout << B<F2>::function_call_operator << std::endl; // false; OK
    std::cout << std::noboolalpha;
}

Here's the online compiled version with C++14 compiler.


Solution

  • When I repeat that with just one template argument, also defaulted, the specialisation seems to be still well formed, explicitly supplying the type which is otherwise default in the base template. However in this case it seems that this is ignored and not considered more specialised. The base version is always chosen.

    Because the specialization, for B, is for int (in F1 and F2 cases)

    template <typename T>
    struct B<decltype(std::declval<T>()())> // decltype(...) is int, in F1 and F2 cases
     { constexpr static bool function_call_operator{true}; };
    

    You instantiate B with F1 and F2. Both of they are different from int.

    Only the generic version matched both B<F1> and B<F2>.

    The B specialization is never choose because T isn't deducible (from B<int> you can't deduce F1 or F2).