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参照。

関数型/関数オブジェクト型*2 Fn と引数型リスト ArgTypes... から、該当呼び出しの戻り値型を取得するメタ関数*3。C++14 Table 57, p4より一部引用。

template <class Fn, class... ArgTypes> struct result_of<Fn(ArgTypes...)>;
Fn and all types in the parameter pack ArgTypes shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound.
If the expression INVOKE(declval<Fn>(), declval<ArgTypes>()...) is well formed when treated as an unevaluated operand (Clause 5), the member typedef type shall name the type decltype(INVOKE(declval<Fn>(), declval<ArgTypes>()...)); otherwise, there shall be no member type. (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部を一部引用。

result_of syntax: the result_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を返す関数型」として扱う。


*1C++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参照。



トラックバック - http://d.hatena.ne.jp/yohhoy/20160728/p1