yohhoyの日記

技術的メモをしていきたい日記

std::result_ofメタ関数のテンプレートパラメータ

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...から、該当呼び出しの戻り値型を取得するメタ関数*3C++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 pack ArgTypes shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound.
Comments:
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部を一部引用。

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以外(functionpackaged_task)では、テンプレートパラメータR(ArgTypes...)を定義通り「引数型ArgTypes...をとって戻り値型Rを返す関数型」として扱う。

関連URL

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