43

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?

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 2
    Ok, just to be clear (after the somewhat complicated answers): your question has nothing to do with the *default argument*, because it is not used in either of the example cases. – davidhigh Sep 06 '19 at 15:45

2 Answers2

43

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.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
29

You need to stop template argument deduction for ARG_T from the function argument v, (with the help of std::type_identity, which could be used to exclude specific arguments from deduction); Otherwise, the default template argument won't be used. e.g.

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

LIVE

BTW: If your compiler doesn't support std::type_identity (since C++20), you might make your own.

template<typename T> struct type_identity { typedef T type; };
template< class T >
using type_identity_t = typename type_identity<T>::type;
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • 4
    This answer should make clear that this is a C++20 feature, that this is 2019, and that not one of the major extant compilers fully supports the features touted for C++20. – David Hammen Sep 07 '19 at 07:53