Stephan T. Lavavejが "Class Template Argument Deduction"のCppCon 2018
で話すのを見たところ、 ある時点 で彼は偶然にこう言います:
C++の型情報はほとんど逆向きに流れません...1つか2つの場合があるので、「ほぼ」と言わなければなりませんでしたが、おそらくもっと多いがごくわずかです。
彼がどの事件について言及しているのか把握しようとしたにもかかわらず、私は何も思いつかなかった。それ故質問:
どの場合に、C++ 17標準は型情報を逆方向に伝播することを義務付けていますか?
これは少なくとも1つのケースです。
struct foo {
template<class T>
operator T() const {
std::cout << sizeof(T) << "\n";
return {};
}
};
foo f; int x = f; double y = f;
を実行すると、型情報が「逆方向」に流れて、T
がoperator T
に何を含んでいるのかがわかります。
これをもっと高度な方法で使うことができます。
template<class T>
struct tag_t {using type=T;};
template<class F>
struct deduce_return_t {
F f;
template<class T>
operator T()&&{ return std::forward<F>(f)(tag_t<T>{}); }
};
template<class F>
deduce_return_t(F&&)->deduce_return_t<F>;
template<class...Args>
auto construct_from( Args&&... args ) {
return deduce_return_t{ [&](auto ret){
using R=typename decltype(ret)::type;
return R{ std::forward<Args>(args)... };
}};
}
だから今私はできる
std::vector<int> v = construct_from( 1, 2, 3 );
そしてそれはうまくいきます。
もちろん、なぜ{1,2,3}
をしないのですか? {1,2,3}
は式ではありません。
std::vector<std::vector<int>> v;
v.emplace_back( construct_from(1,2,3) );
確かに、もう少し魔法使いが必要です: 実例 。 (私はdeduce returnにFのSFINAEチェックを行わせ、そしてFをSFINAEフレンドリーにしなければなりません。 そして deduce_return_t演算子Tのstd :: initializer_listをブロックしなければなりません。)
Stephan T. Lavavej氏がツイートで話していた事例について説明した :
私が考えていたのは、あなたがオーバーロードされた/テンプレート化された関数のアドレスを取得することができ、それが特定の型の変数を初期化するために使われている場合です。 (曖昧さを解消するもののリストがあります。)
cppreferenceページのAddress:オーバーロードされた関数のアドレス から、この例を見ることができます。
int f(int) { return 1; }
int f(double) { return 2; }
void g( int(&f1)(int), int(*f2)(double) ) {}
int main(){
g(f, f); // selects int f(int) for the 1st argument
// and int f(double) for the second
auto foo = []() -> int (*)(int) {
return f; // selects int f(int)
};
auto p = static_cast<int(*)(int)>(f); // selects int f(int)
}
具象型の初期化だけではありません。引数の数から推測することもできます
そして この実例 :を提供します。
void overload(int, int) {}
void overload(int, int, int) {}
template <typename T1, typename T2,
typename A1, typename A2>
void f(void (*)(T1, T2), A1&&, A2&&) {}
template <typename T1, typename T2, typename T3,
typename A1, typename A2, typename A3>
void f(void (*)(T1, T2, T3), A1&&, A2&&, A3&&) {}
int main () {
f(&overload, 1, 2);
}
私はもう少し詳しく説明します もっとここに 。
オーバーロードされた関数の静的キャストでは、フローは通常のオーバーロードの解決方法とは逆の方向に進みます。それで、それらの1つは後ろ向きです、と私は思います。