[base/functional/invoke.h](https://source.chromium.org/chromium/chromium/src/+/main:base/functional/invoke.h)がいいお勉強素材っぽいので読む。
このファイルはいろんなヘルパーが実装されているっぽい。
## メンバ関数ポインタからクラスの型をゲット
以下は`member_pointer_class_t<T>`でT型がいるクラスを取るためのテンプレート
```cpp=
template <typename DecayedF>
struct member_pointer_class {};
template <typename ReturnT, typename ClassT>
struct member_pointer_class<ReturnT ClassT::*> {
using type = ClassT;
};
template <typename DecayedF>
using member_pointer_class_t = typename member_pointer_class<DecayedF>::type;
```
`member_pointer_class`は2つの定義を持つ。
2つ目の方は`ReturnT ClassT::*`でオーバーロードしている。
この表記は馴染みがなかったが、`ReturnT`型メンバ変数へのポインタである。
なおメンバ変数へのポインタはメンバ関数ポインタもOK。
[member access operator](https://en.cppreference.com/w/cpp/language/operator_member_access)を確認すると以下のような例がある。
```cpp=
struct A { int i; };
struct B { void f(); };
int A::* mp = &A::i;
void (B::*mpf)() = &B::f;
```
pointer-to-memberアクセス演算子という名前で紹介されていた。
これと踏まえた上でもう一度2つ目の`member_pointer_class`を見てみる。
```cpp=
template <typename ReturnT, typename ClassT>
struct member_pointer_class<ReturnT ClassT::*> {
using type = ClassT;
};
```
`member_pointer_class<F>`のように渡したF型は`ReturnT ClassT::*`になり`ReturnT`と`ClassT`の置き換えにトライする。
置き換えができたなら`ClassT`が`F`を含むクラスになるので`type`を`ClassT`として宣言し、`::type`からクラス型を取れるようになった。
一方で`ReturnT`と`ClassT`の置き換えが出来ないケースもある。
メンバ変数ポインタはクラスにしか定義されない演算子なので、もし`F`がクラスのメンバでなければ置き換え失敗し、SFINAEのやつになる。
```cpp=
template <typename DecayedF>
struct member_pointer_class {};
```
フォールバック先は空構造体。
そして`typename member_pointer_class<DecayedF>::type`と`type`値にアクセスする`member_pointer_class_t`を使うと`type`が存在しているときにしかオーバーロードされなくなる。
この場合`type`はクラスである。
### IsMemFunPtr
では上で言及した`member_pointer_class_t`の使用例を確認する。
```cpp=
template <typename F,
typename T,
typename MemPtrClass = member_pointer_class_t<std::decay_t<F>>>
const bool& IsMemPtrToBaseOf =
std::is_base_of<MemPtrClass, std::decay_t<T>>::value;
```
[`std::is_base_of<Base, Derived>`](https://en.cppreference.com/w/cpp/types/is_base_of)は既出だがBase型がDerived型のBaseかを調べ、std:true_typeかstd::false_typeを返す。
T型は[`std::decay_t`](https://en.cppreference.com/w/cpp/types/decay)を通ることでなんかいい感じの型になる。関数型は関数ポインタに変換されている。
というわけで型を渡すとT型の継承元に関数型Fがいるかを調べている。
`IsMemPtrToBaseOf`の使用例は以下。
```cpp=
template <typename F,
typename T1,
typename... Args,
EnableIf<IsMemFunPtr<F> && IsMemPtrToBaseOf<F, T1>> = true>
constexpr decltype(auto) InvokeImpl(F&& f, T1&& t1, Args&&... args) {
return (std::forward<T1>(t1).*f)(std::forward<Args>(args)...);
}
```
EnableIfの文で呼び出し先として適切か確認していそう。
## Reference wrapper
```cpp=
template <typename T>
struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T>
const bool& IsRefWrapper = is_reference_wrapper<std::decay_t<T>>::value;
```
だいぶ読みやすくなった。
[`std::reference_wrapper`](https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper)は名前の通り参照オブジェクトにラップしてくれる。
参照オブジェクトに置き換え可能だったら`std::true_type`を返す、つまり渡した型が参照かを確認しているだけ。
## decltype(auto)
こんな`invoke`テンプレートがあった。
```cpp=
template <typename F, typename... Args>
constexpr decltype(auto) invoke(F&& f, Args&&... args) {
return internal::InvokeImpl(std::forward<F>(f), std::forward<Args>(args)...);
}
```
`decltype(auto)`というのは初めて見た。
[`decltype`](https://cpprefjp.github.io/lang/cpp11/decltype.html)はオペランドで指定した式の型を取得する。
```cpp=
int i = 0;
decltype(i) j = 0;
```
このように`j`の型がintだと指定することができる。
ではautoをいれるとどうなる?
これ専用のreferenceがあった。-> [decltype(auto)](https://cpprefjp.github.io/lang/cpp14/decltype_auto.html)によると
```cpp=
int a = 3;
int b = 2;
decltype(auto) d = a + b;
```
こんな感じで右辺の式で置き換えて型推論する機能だった。
[通常関数の戻り値推論](https://cpprefjp.github.io/lang/cpp14/return_type_deduction_for_normal_functions.html)に使用できる。
## Note
### is_member_object_pointer
IsMemPtrToBaseOfの他に以下のようなヘルパー関数もある。
```cpp=
template <typename F>
const bool& IsMemFunPtr =
std::is_member_function_pointer<std::decay_t<F>>::value;
template <typename F>
const bool& IsMemObjPtr = std::is_member_object_pointer<std::decay_t<F>>::value;
```
[`is_member_object_pointer`](https://en.cppreference.com/w/cpp/types/is_member_object_pointer)は型Tがメンバ変数へのポインタ型かを調べ、std:true_typeかstd::false_typeを返す。
[`std::is_base_of`](https://en.cppreference.com/w/cpp/types/is_base_of)でも出てきたが、std:true_typeやstd::false_typeはコンパイル時の条件式を扱うために導入されたヘルパー型。