あなたのコードでは、 の 2 つの異なる使用方法がありますauto。1 つはパラメーターで、もう 1 つは戻り値の型です。パラメーターの場合、を使用するたびautoに一意のテンプレート型引数が導入されるため、Jerry が言及しているように、次のようになります。
// 1
template <typename A, typename B>
auto add(A a, B b) {
return a + b;
}
この場合、異なる型引数に何らかの制約がある場合 (1 つが同じである必要がありますが、他にもあるはずです)、明示的なテンプレート構文はより良い代替手段を提供します。これは、(追加のテンプレート引数を使用して) 引数で SFINAE を使用する場合に特に当てはまります。
// 2
template <typename A, typename B,
typename _1 = typename A::iterator, // A has nested iterator type
typename _2 = typename B::iterator> // So does B
auto f(A a, B b);
戻り値の型での SFINAE の使用を意図的に避けていることに注意してください。これは、 の他の使用を何らかの形で妨害するためですautoが、これは別のオプションになる可能性があります。
// 3
auto f(auto a, auto b)
-> typename enable_if<has_nested_iterator<decltype(a)>::value
&& has_nested_iterator<decltype(b)>::value,
[return type] >::type;
しかし、ご覧のとおり、末尾の戻り値の型を使用し、 を使用して値から型を取得する必要があるため、もう少し複雑になりますdecltype。
あなたの例での の 2 番目の使用法はauto、これとはかなり異なりますが、戻り値の型に推定された戻り値の型を持たせることです。推定される戻り値の型は、C++11 でラムダに対して既に使用可能だった機能ですが、すべての関数テンプレートに一般化されています。returnこの機能により、コンパイラは本体内のさまざまなステートメントを調べることで、関数によって返される型を見つけることができます。利点は、テンプレートに 1 つの return 式がある場合、その式を 2 回入力する必要がなくなることです。1 つは戻り値の型で、もう 1 つは実際のコードです。
// 4
auto add(auto a, auto b) -> decltype(a + b) { // 'a + b' here
return a + b; // 'a + b' also here
}
欠点は、返される型を決定するためにコンパイラが関数の本体を検査する必要があることです。これは必然的に型置換の後になります。そのため、推定型を持つ関数からの return ステートメントは SFINAE 式で使用できず、関数のユーザーの生活を複雑にする可能性があります。
// 5
auto doAdd(auto a, auto b)
-> typename enable_if<is_integral<decltype(add(a,b))>>::type
{
return add(a,b);
}
SFINAE はオーバーロード解決セットから上記のオーバーロードを削除せず、 として呼び出すとハード エラーが発生します。doAdddoAdd(1, 1.)