Search code examples
c++templatesfunction-templatestemplate-argument-deduction

Template default argument loses its reference type


Consider

#include <iostream>
#include <type_traits>

template <class T, class ARG_T = T&>
T foo(ARG_T v){
    return std::is_reference<decltype(v)>::value;
}

int main() {
    int a = 1;
    std::cout << foo<int>(a) << '\n';
    std::cout << foo<int, int&>(a) << '\n';
}

I'd expect the output to be 1 in both cases. But in the first case it's 0: consistent with the default being class ARG_T = T rather than class ARG_T = T&.

What am I missing?


Solution

  • For foo<int>(a), ARG_T is being deduced from a, and is not taken from the default template argument. Since it's a by value function parameter, and a is an expression of type int, it's deduced as int.

    In general, default template arguments are not used when template argument deduction can discover what the argument is.

    But we can force the use of the default argument by introducing a non-deduced context for the function parameter. For instance:

    template <class T, class ARG_T = T&>
    T foo(std::enable_if_t<true, ARG_T> v1){
        //...
    }
    

    Or the C++20 type_identity utility, such as the other answer demonstrates.