私は実際には同じことに少し興味があります(最後の引数に基づいてテンプレート化されたパラメーターパックを特殊化したいと思っています)。
タプルの反転( 、 C ++ 14のstd::make_tuple
バックポートなど)を組み合わせることで、前進する道があると思います。std::apply
成功すればここに戻ります。
関連記事:
編集:うん、少し後にそれを理解した。余分なコピーが飛び交っているので完璧ではありませんが、それは始まりです。
以下にリストするよりも簡単な方法をご存知の場合は、遠慮なく投稿してください。
TL; DR
このようなことを行うことができます:
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
そして、このpseduoコードを実装します。
template<typename ... Args>
void my_func(Args&& ... args, const my_special_types& x);
次のようなことを行うことによって:
template<typename... Args>
void my_func(Args&& ... args)
-> call my_func_reversed(args...)
template<typename... RevArgs>
void my_func_reversed(const my_special_types& x, RevArgs&&... revargs)
-> do separate things with revargs and my_special_types
-> sub_func_reversed(revargs...)
上記のユーティリティを使用します。
いくつかの(多くの)欠点があります。それらを以下にリストします。
範囲
これは、将来(C ++ 17)から借りたいC ++ 14(おそらくC ++ 11)のユーザー向けです。
ステップ1:引数を逆にする
これを行うには、いくつかの異なる方法があります。この例では、いくつかの選択肢をリストアップしました。
- tuple.cc -2つの選択肢の遊び場(ソースコードのクレジット):
- 折りたたみ式を使用し、
std::apply_impl
(クレジット:Orient)を介して渡されたインデックスを操作します。
- 再帰テンプレートを使用して、反転を作成します
index_sequence
(クレジット:Xeo)
tuple.output.txt-出力例
これによりreversed_index_sequence
、Xeoの例からテンプレートが出力されます。デバッグのためにこれが必要でした。
>>> name_trait<std::make_index_sequence<5>>::name()
std::index_sequence<0, 1, 2, 3, 4>
>>> name_trait<make_reversed_index_sequence<5>>::name()
std::index_sequence<4, 3, 2, 1, 0>
消化しやすいので、Alternative1を選びました。それから私はそれをすぐに形式化しようとしました:
定義スニペット(cppreference.comでのC ++ 17の可能な実装のstd::apply
適応):
namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_reversed_impl(F &&f,
Tuple &&t, std::index_sequence<I...>)
{
// @ref https://stackoverflow.com/a/31044718/7829525
// Credit: Orient
constexpr std::size_t back_index = sizeof...(I) - 1;
return f(std::get<back_index - I>(std::forward<Tuple>(t))...);
}
} // namespace detail
template <class F, class Tuple>
constexpr decltype(auto) apply_reversed(F &&f, Tuple &&t)
{
// Pass sequence by value to permit template inference
// to parse indices as parameter pack
return detail::apply_reversed_impl(
std::forward<F>(f), std::forward<Tuple>(t),
std::make_index_sequence<
std::tuple_size<std::decay_t<Tuple>>::value>{});
}
使用法スニペット:(からtuple_future_main.output.txt
、上からコピー)
auto my_func_callable = [] (auto&& ... args) {
return my_func(std::forward<decltype(args)>(args)...);
};
auto my_func_reversed =
stdcustom::make_callable_reversed(my_func_callable);
ステップ2:靴を締める(パラメーターパックを逆にして)
まず、使用する最後の引数のパターンを確立します。パラメータパックは1つしか持てないため、これらを明示的に列挙する必要があります。
(tuple_future_main.ccから取得):
シナリオ例:
私たちは、次のような名前のコンテナに物を追加するのが好きです。
add_item(const Item& item, const string& name, Container& c)
[非常に多くの]オーバーロードを含むアイテムを作成することもでき、便利なオーバーロードがあります。
add_item(${ITEM_CTOR_ARGS}, const string& name, Container& c)
そのために、次のことを宣言できます。
void add_item_direct(const Item& item, const string& name, Container& c)
Item create_item(Args&&... args)
次に、ジェネリックインターフェイスを定義します。
template<typename... Args>
void add_item(Args&&... args) {
...
auto reversed = stdcustom::make_callable_reversed(callable);
reversed(std::forward<Args>(args)...);
}
template<typename ... RevArgs>
void add_item_reversed(Container& c, const string& name, RevArgs&&... revargs)
{
...
static auto ctor = VARIADIC_CALLABLE(create_item,);
...
auto item = ctor_reversed(std::forward<RevArgs>(revargs)...);
add_item_direct(item, name, c);
}
今、私たちは次のようなことをすることができます:(から取得tuple_future_main.output.txt
)
>>> (add_item(Item("attribute", 12), "bob", c));
>>> (add_item("attribute", 12, "bob", c));
>>> (add_item(Item(2, 2.5, "twelve"), "george", c));
>>> (add_item(2, 2.5, "twelve", "george", c));
>>> (add_item(Item(2, 15.), "again", c));
>>> (add_item(2, 15., "again", c));
>>> c
bob - ctor3: ctor3: ctor1: attribute (12, 10)
bob - ctor3: ctor1: attribute (12, 10)
george - ctor3: ctor3: ctor2: 2, 2.5 (twelve)
george - ctor3: ctor2: 2, 2.5 (twelve)
again - ctor3: ctor3: ctor2: 2, 15 ()
again - ctor3: ctor2: 2, 15 ()
余分なコピーコンストラクターに注意してください...:(
欠点
- 地獄のように醜い
- 役に立たないかもしれません
- インターフェイスをリファクタリングする方が簡単かもしれません
- ただし、これは、より一般化されたインターフェイスに移行するための一時的なギャップとして使用できます。
- 削除する行が少なくなる可能性があります。
- 特に、開発プロセスがテンプレートの爆発的な増加につながる場合
- 余分なコピーがどこから来ているのかを特定することはできません。
- 可変個引数ラムダの賢明な使用が原因である可能性があります
- 基本機能を慎重に作成する必要があります
- 既存の関数を拡張しようとしないでください。
- パラメータパックは、関数との一致に貪欲になります
- 必要な各オーバーロードを明示的に説明するか、お辞儀をして可変個引数パラメーターパックを目的の機能にディスパッチさせる必要があります。
- これを回避するためのエレガントな方法を見つけたら、私に知らせてください。
- テンプレートエラーはくだらないです。
- 確かに、くだらないことではありません。しかし、利用可能な過負荷を逃したと推測するのは難しいです。
- 多くの単純な関数をラムダでラップします
make_reversed_index_sequence
関数を使用して直接ディスパッチできる場合があります(他のSOの投稿に記載されています)。しかし、それを繰り返すのは苦痛です。
Todo
- 余分なコピーを取り除く
- すべてのラムダの必要性を最小限に抑える
- あなたが持っている場合は必要ありません
Callable
パラメータパックの貪欲と戦ってみてください
std::enable_if
左辺値参照と右辺値参照の両方に一致し、互換性のある暗黙的なコピーコンストラクターの転送を処理する可能性のある一般化された一致はありますか?
template<typename ... Args>
void my_func(Args&& ... args) // Greedy
void my_func(magical_ref_match<string>::type, ...)
// If this could somehow automatically snatch `const T&` and `T&&` from the parameter pack...
// And if it can be used flexible with multiple arguments, combinatorically
希望
- たぶん、C ++ 17は非最終的なパラメータパック引数をサポートするので、これらすべてを破棄することができます...指が交差しました