Аргумент шаблона по умолчанию теряет свой ссылочный тип

Рассмотрим

#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';
}

Я ожидаю, что результат будет 1 в обоих случаях. Но в первом случае это 0: соответствует по умолчанию class ARG_T = T, а не class ARG_T = T&.

Что мне не хватает?

Ответ 1

Для foo<int>(a) ARG_T выводится из a и не берется из аргумента шаблона по умолчанию. Поскольку это параметр функции по значению, а a является выражением типа int, он выводится как int.

Как правило, аргументы шаблона по умолчанию не используются, когда вычет аргумента шаблона может определить его аргумент.

Но мы можем форсировать использование аргумента по умолчанию, введя невыгруженный контекст для параметра функции. Например:

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

Или утилита С++ 20 type_identity, как показано в другом ответе.

Ответ 2

Вам нужно остановить вывод аргумента шаблона для ARG_T из аргумента функции v, (с помощью std::type_identity, который можно использовать для исключения определенных аргументов из вывода ); В противном случае аргумент шаблона по умолчанию не будет использоваться. например,

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

ЖИВОЙ

Кстати: если ваш компилятор не поддерживает std::type_identity (начиная с С++ 20), вы можете сделать свой собственный.

template<typename T> struct type_identity { typedef T type; };
template< class T >
using type_identity_t = typename type_identity<T>::type;