我々は、引数の任意の順序を受け入れることができ、テンプレート機能を加えることができます、しかし単一のタイプのインスタンスを持つように制約されたC++のパラメータパック? C++ 11以降
template <typename... Ts>
void func(Ts &&... ts) {
step_one(std::forward<Ts>(ts)...);
step_two(std::forward<Ts>(ts)...);
}
をそれは本当に唯一の各引数は、同じ型を持っている場合には、私の関数を呼び出すことは理にかなっているとし - 引数はいくつでもOKです。
その場合、最適な方法は、テンプレートがその場合にエラーメッセージを表示するようにテンプレートを制約するか、引数が一致しないときにfunc
が過負荷解決に参加しないようにすることです。
それが助け場合、私はそれが本当に具体的なことができます:
私はいくつかの構造体があるとします。
struct my_struct {
int foo;
double bar;
std::string baz;
};
さて、私のようなことを行うことができるようにしたい、のメンバーを印刷デバッグ目的のための構造体、構造体のシリアライズとデシリアライズ、構造体のメンバーへの順番付けなどがあります。
template <typename V>
void apply_visitor(V && v, my_struct & s) {
std::forward<V>(v)("foo", s.foo);
std::forward<V>(v)("bar", s.bar);
std::forward<V>(v)("baz", s.baz);
}
template <typename V>
void apply_visitor(V && v, const my_struct & s) {
std::forward<V>(v)("foo", s.foo);
std::forward<V>(v)("bar", s.bar);
std::forward<V>(v)("baz", s.baz);
}
template <typename V>
void apply_visitor(V && v, my_struct && s) {
std::forward<V>(v)("foo", std::move(s).foo);
std::forward<V>(v)("bar", std::move(s).bar);
std::forward<V>(v)("baz", std::move(s).baz);
}
(それは、このようなコードを生成するために、少し面倒に見えますが、私はそれを助けるためにいくつかの時間前a small libraryを作った。)
だから、それはmy_struct
の2つのインスタンスを訪れることができるように、今、私はそれを拡張したいと思います同時に。それを使用するのは、等価または比較操作を実装する場合です。 boost::variant
のドキュメントでは、「単元訪問」とは対照的に、「バイナリ訪問」と呼ばれています。
おそらく、誰もバイナリ訪問以外のことをしたいとは思わないでしょう。しかし、私は一般的にn-ary
訪問のようにしたいと思う。その後、それはこのようになります私は
template <typename V, typename ... Ss>
void apply_visitor(V && v, Ss && ... ss) {
std::forward<V>(v)("foo", (std::forward<Ss>(ss).foo)...);
std::forward<V>(v)("bar", (std::forward<Ss>(ss).bar)...);
std::forward<V>(v)("baz", (std::forward<Ss>(ss).baz)...);
}
を推測しかし、今、それはもう少しsquirrellyなってきた - 誰かがすべてでさえも同じ構造のタイプではありません種類のシリーズを通過すれば、コードはまだコンパイルしますユーザーがまったく予期せぬことをする。
私はこのようにそれをやって考えた:
template <typename V, typename ... Ss>
void apply_visitor(V && v, Ss && ... ss) {
auto foo_ptr = &my_struct::foo;
std::forward<V>(v)("foo", (std::forward<Ss>(ss).*foo_ptr)...);
auto bar_ptr = &my_struct::bar;
std::forward<V>(v)("bar", (std::forward<Ss>(ss).*bar_ptr)...);
auto baz_ptr = &my_struct::baz;
std::forward<V>(v)("baz", (std::forward<Ss>(ss).*baz_ptr)...);
}
彼らはミスマッチの種類とそれを使用する場合、少なくともコンパイルエラーの原因となること。しかし、それはまた遅すぎて起こっています - テンプレートタイプが解決された後に起こっています、そして、オーバーロード解決の後、私は推測します。
voidを返す代わりにstd::enable_if_t
を使用し、パラメータパックの各タイプについて式std::is_same<std::remove_cv_t<std::remove_reference_t<...>>
を調べて、SFINAEを使用することを考えました。
SFINAEの式はかなり複雑で、2つの理由で欠点もあります。派生クラスstruct my_other_struct : my_struct { ... }
があり、ビジターのメカニズムで使用したいと考えているため、一部のパラメータmy_struct
であり、一部はmy_other_struct
です。理想的には、システムはすべての参照をmy_struct
に変換し、その方法で訪問者を適用し、afaikはメンバーポインタfoo_ptr
、bar_ptr
、baz_ptr
で上記の例を示しましたが、そこに正しいことをするでしょうが、 SFINAEのような制約 - 私が推測するすべてのパラメータの共通基盤を見つけることを試みなければならないだろうか?
これらの懸案事項を一般的に調整する良い方法はありますか?
'std :: common_type'? – cpplearner
あなたはそれらがすべて特定の既知のタイプであること、あるいは同じ、未知のタイプであることを希望しますか? – Quentin
@cpplearner:ああ、それはとてもいいですね、私はそれについては知らなかった –