2

次のような状況でf関数の引数を関数に転送する良い方法はありますかg

template<typename... T>
void g(T && ... x);

template<typename... T>
void f(T && ... x)
{
    g(x..., x...);
}

次のコードでは、 x2 回移動できます

template<typename... T>
void f(T && ... x)
{
    g(std::forward<T>(x)..., std::forward<T>(x)...);
}

次のコードstd::forward<T>(x)...では、前に評価できますx...

template<typename... T>
void f(T && ... x)
{
    g(x..., std::forward<T>(x)...);
}
4

2 に答える 2

2

一般に、次を使用して注文を強制できます

  • 別々のステートメントを使用する(明らかに)
  • コンマ演算子で区切られた式。(オーバーロードに注意operator,)
  • ブレース初期化子リスト内の引数の評価順序は、それらが出現する順序であるため、ブレース初期化の使用は機能します1。以下には、明確に定義された評価順序があります。

    std::tuple<T..., T...> args { 
         std::forward<T>(x)..., 
         std::forward<T>(x)... }; // still not sane, but evaluation order defined
    

しかしg(...) 、同じ参照から 2 回移動する可能性があるため、それでも役に立ちません。右辺値参照に実際に必要なのはとおりです。

g(rvalue, std::move(rvalue));            // or
g(std::move(rvalue), rvalue);            // or even
g(std::move(rvalue), std::move(rvalue)); // [sic]

唯一の正気の方法は次のとおりです。

g(lvalue=std::move(rvalue), lvalue); // BUT: fix the evaluation order

では、どうすればそれを正確に しかし一般的に達成できるのでしょうか?

インデックスを入力してください?!

あなたが説明したように可変長を持っ​​ているとしましょうg

template<typename... T>
void g(T && ... x) 
{ 
}

fこれで、渡された引数を使用して複製できます

  1. インデックスのトリック:

    namespace detail // indices 
    {
        template<std::size_t... Is> struct seq{};
        template<std::size_t I, std::size_t... Is>
            struct gen_seq : gen_seq<I-1, I-1, Is...>{};
        template<std::size_t... Is>
            struct gen_seq<0, Is...>{ using type = seq<Is...>; };
    }
    
  2. およびインボーカー ヘルパー関数:

    #include <tuple>
    
    template<typename Tuple, std::size_t... Is>
    void f_invoke_helper(Tuple const& tup, detail::seq<Is...>) 
    { 
        g(std::get<Is>(tup)..., std::get<Is>(tup)...);
    }
    

次に必要なのは、すべてを結び付けることだけです。

template<typename... T>
void f(T && ... x)
{
    f_invoke_helper(
            std::make_tuple(std::forward<T>(x)...), 
            typename detail::gen_seq<sizeof...(T)>::type());
}

rvalue-refs を渡すと、 (タプルに) 1 回移動され、invoker ヘルパーで (左辺値として) 2 回使用されることに注意してください。

int main()
{
    std::string x = "Hello world";
    int i = 42;

    // no problem:
    f(i, -42, std::move(x));
}

お役に立てれば!

PS。適切に指摘されているので、おそらく言うのはずっと簡単です

template<typename... T>
void f(T&&... x) { g(x..., x...); }

実際に移動可能な引数をタプルに移動することを除いて、タプルイディオムが同じ結果にならない方法は考えていません。


1 T{...} のセマンティクスは 12.6.1 で説明されています。
参照: std::make_tuple を使用するときにコンストラクターの未定義の実行順序を回避する方法

于 2013-07-16T21:01:02.893 に答える