C++11標準ライブラリで導入されたstd::result_of<Fn(ArgTypes...)>
メタ関数*1のテンプレートパラメータ部に関するメモ。
2017-10-06追記:C++1z(C++17)ではstd::result_of<Fn(ArgTypes...)>
メタ関数は非推奨(deprecated)とされ、替わりにstd::invoke_result<Fn, ArgTypes...>
メタ関数が追加される。経緯はP0604R0参照。
2021-07-12追記:C++20標準ライブラリではstd::result_of<Fn(ArgTypes...)>
メタ関数は削除済み。P0619R4参照。
関数型/関数オブジェクト型*2Fn
と引数型リストArgTypes...
から、該当呼び出しの戻り値型を取得するメタ関数*3。C++14 20.10.7.6 Table 57, p4より一部引用。
Template:
template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;
Condition:
Fn
and all types in the parameter packArgTypes
shall be complete types, (possibly cv-qualified)void
, or arrays of unknown bound.
Comments:
If the expressionINVOKE(declval<Fn>(), declval<ArgTypes>()...)
is well formed when treated as an unevaluated operand (Clause 5), the member typedeftype
shall name the typedecltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...));
otherwise, there shall be no membertype
. (snip)
4 [Example: Given these definitions:
typedef bool (&PF1)(); typedef short (*PF2)(long); struct S { operator PF2() const; double operator()(char, int&); void fn(long) const; char data; }; typedef void (S::*PMF)(long) const; typedef char S::*PMD;the following assertions will hold:
static_assert(is_same<result_of_t<S(int)>, short>::value, "Error!"); static_assert(is_same<result_of_t<S&(unsigned char, int&)>, double>::value, "Error!"); static_assert(is_same<result_of_t<PF1()>, bool>::value, "Error!"); static_assert(is_same<result_of_t<PMF(unique_ptr<S>, int)>, void>::value, "Error!"); static_assert(is_same<result_of_t<PMD(S)>, char&&>::value, "Error!"); static_assert(is_same<result_of_t<PMD(const S*)>, const char&>::value, "Error!");-- end example]
result_ofメタ関数に与えるテンプレートパラメータFn(ArgTypes...)
は、関数型/関数オブジェクト型Fn
を型リストArgTypes...
で呼び出す構文のように見える。しかしC++言語仕様の構文規則上はFn(ArgTypes...)
全体で関数型(function type)を宣言しておりFn
部分は同関数型の戻り値型として解釈される。この「引数型ArgTypes...
をとって戻り値型Fn
を返す関数型」それ自体は意味を持たず、タグ・ディスパッチ手法のためのタグ型として扱われる。初期の提案文書N1454よりRationale部を一部引用。
Rationale
result_of
syntax: theresult_of
syntax differs from existing practice. Existing libraries generally pass the argument types as individual template arguments or via a tuple-like typelist facility. However, it should be noted that these libraries predate the use of function types to encode operations and argument lists. There are several other advantages to the function type encoding:
- The syntax of the type computation closely mirrors the syntax of the run-time computation.
- The function type includes in a natural way the type of the function object (even when forwarding to the
result
member class template), allowing for differentiation between invocations of a function object with differing cv-qualifiers.- Limitations on the number of argument types are not introduced by the
result_of
class template.- Auxiliary typelist types are not introduced.
- With the acceptance of the function object wrappers proposal, there is precedent for the user of function types to encode function argument lists.
メモ:C++標準ライブラリでもresult_of
以外(function
やpackaged_task
)では、テンプレートパラメータR(ArgTypes...)
を定義通り「引数型ArgTypes...
をとって戻り値型R
を返す関数型」として扱う。
関連URL
- N1454 A uniform method for computing function object return types (revision 1)
- c++ - Why does std::result_of take an (unrelated) function type as a type argument? - Stack Overflow
- c++ - What's the difference between result_of<F(Args...> and decltype<f(args...)>? - Stack Overflow
- cppreference: result_of, cpprefjp: result_of
*1:C++14標準ライブラリでは std::result_of_t<Fn(ArgTypes...)> エイリアステンプレートが追加された。std::result_of<Fn(ArgTypes...)>::type の代わりに std::result_of_t<Fn(ArgTypes...)> と記述できる。
*2:厳密にはメンバ関数へのポインタ型 R (C::*)(ArgsTypes...) とデータメンバへのポインタ型 T (C::*) も受容する。この場合、ArgTypes... の第一引数は同クラスまたはポインタ型に、それ以降をメンバ関数呼び出しの引数型として解釈する。(→id:yohhoy:20141106)
*3:C++11時点では Fn に呼び出し可能な型(callable type)を要求していた。C++14以降はこの要件が緩和され、SFINAEに利用できるようになった。詳細はN3462参照。