yohhoyの日記

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

INVOKE仮想操作の参照実装

C++11で定義される INVOKE 仮想操作の参照実装。C++1z(C++17)に向けてN4169C++標準ライブラリ入りを提案中。

2016-07-28追記:C++1z(C++17)標準ライブラリへのstd::invoke関数テンプレート追加が採択された。

2019-09-14追記:C++2a(C++20)標準ライブラリでは INVOKE 仮想操作をconstexpr対応するため、提案文書P1065R2が採択された。

// N4169 Implementabilityより引用
#include <functional>
#include <type_traits>
#include <utility>

template<typename Functor, typename... Args>
typename std::enable_if<
  std::is_member_pointer<typename std::decay<Functor>::type>::value,
  typename std::result_of<Functor&&(Args&&...)>::type
>::type invoke(Functor&& f, Args&&... args)
{
  return std::mem_fn(f)(std::forward<Args>(args)...);
}

template<typename Functor, typename... Args>
typename std::enable_if<
  !std::is_member_pointer<typename std::decay<Functor>::type>::value,
  typename std::result_of<Functor&&(Args&&...)>::type
>::type invoke(Functor&& f, Args&&... args)
{
  return std::forward<Functor>(f)(std::forward<Args>(args)...);
}

ファンクタfがメンバ関数/データメンバへのポインタ型の場合(20.8.2/p1/b1-4)、std::mem_fnクラステンプレートへ処理を委譲している。C++11(N3337) 20.8.2/p1, 20.8.10/p1より一部引用。

Define INVOKE(f, t1, t2, ..., tN) as follows:

  • (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;
  • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;
  • t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;
  • (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 is not one of the types described in the previous item;
  • f(t1, t2, ..., tN) in all other cases.

template<class R, class T>
unspecified mem_fn(R T::* pm);
template<class R, class T, class... Args>
unspecified mem_fn(R (T::* pm)(Args...));
(snip)
Returns: A simple call wrapper (20.8.1) fn such that the expression fn(t, a2, ..., aN) is equivalent to INVOKE(pm, t, a2, ..., aN) (20.8.2). (snip)

ノート:これは単なるたらい回し実装に過ぎない。部品から組み上げる場合はlibstdc++-v3実装, libcxx実装などを参照のこと。

関連URL