std::result_of<T>
タイプである必要T
がありますが、任意のタイプではありません。T
は関数型でなければならないため、この部分的な特殊化result_of
が使用されます。
template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;
そのような:
decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...))
整形式です (C++11 20.9.7.6)。(INVOKE は 20.8.2 で定義されています。)
std::result_of<f(int)>
機能しない理由f
は、型ではないためです。関数型のインスタンスです。an に適用されたx
の戻り値の型であることを宣言するには、次のように記述します。f
int
decltype(f(int{})) x;
または、ハードコーディングを好む場合int
:
decltype(f(32)) x;
のタイプf
が必要な場合は、次を使用します。
using FuncPtr = decltype(f);
ただし、提供されたコードF
(つまり、小文字ではないf
) は型であるため、 を引数として受け入れるF(int)
関数を表す型を定義します。明らかに、これは F とは異なります。の型は、インスタンスが関数呼び出し演算子を使用できる構造体です。などを取る明示的または暗黙的なコンストラクターもありません。これはどのように機能しますか?簡単な答え: テンプレート「魔法」。F
int
F
F
int
基本的に、 の定義は型をstd::result_of
取り、F(int)
戻り値の型を引数の型から分離するので、INVOKE() のどのケースが機能するかを判断できます。INVOKE のケースは次のとおりです。
- F は、あるクラス T のメンバー関数へのポインターです。
- 引数が 1 つしかない場合、F はクラス T のデータ メンバーへのポインター、または
- F のインスタンスは、関数として使用できます。つまり、
declval<F>()(declval<int>())
これは、通常の関数呼び出しまたは何らかのタイプのファンクター (たとえば、例のように) にすることができます。
これが決定されるresult_of
と、有効な式の戻り値の型を決定できます。これは、result_of
のtype
メンバーを介して返されるものです。
これの素晴らしいところは、 のユーザーがresult_of
これが実際にどのように機能するかについて何も知る必要がないことです。理解しなければならない唯一のことはresult_of
、関数 TYPE が必要なことです。コード内で型ではない名前を使用している場合 (例: f
)、そのdecltype
ような式の型を取得するために使用する必要があります。
f
最後に、が型と見なされない理由の一部は、テンプレート パラメーターも定数値を許可f
し、定数関数ポインター値であるためです。これは簡単に実証できます (質問の の定義を使用f
):
template <double Op(int)>
double invoke_op(int i)
{
return Op(i);
}
以降:
std::cout << invoke_op<f>(10) << std::endl;
したがって、式の戻り値の型を適切に取得するには、次のように記述f
します。int
decltype(f(int{}))
(注:f
が呼び出されることはありません。コンパイラは単に内部の式を使用して、decltype
その結果、つまりこのインスタンスの戻り値を決定します。)