web-dev-qa-db-ja.com

テンプレートのデフォルト引数は参照タイプを失います

検討する

#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&です。

何が欠けていますか?

43
Bathsheba

foo<int>(a)ARG_Taから推定されており、デフォルトのテンプレート引数から取得されていません。これは値渡しの関数パラメーターであり、aint型の式であるため、intと推定されます。

一般に、デフォルトのテンプレート引数は、テンプレート引数の推定により引数が何であるかを検出できる場合は使用されません。

ただし、関数パラメーターに非推定コンテキストを導入することで、デフォルト引数の使用を強制できます。例えば:

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

またはC++ 20 type_identityユーティリティなど、他の回答が示しています。

関数の引数vからARG_Tの-​​ テンプレート引数の控除 を停止する必要があります( 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をサポートしていない場合(C++ 20以降)、独自に作成する場合があります。

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