4

STL アルゴリズムの使用例は、次のようなリスト初期化コンテナーで示されていることがよくあります。

std::vector< int > v{1, 2, 3, 4};

しかし、このアプローチが(sとは異なり) ( s とは異なり)クラスintに使用される場合、たとえ右辺値( move to ) で渡されたとしても、 1 の過剰なコピー操作を意味std::initializer_listしますconst_iterator

この問題を解決するために、次の (C++17) アプローチを使用します。

template< typename Container, typename ...Args >
Container make_container(Args &&... args)
{
    Container c;
    (c.push_back(std::forward< Args >(args)), ...);
    // ((c.insert(std::cend(c), std::forward< Args >(args)), void(0)), ...); // more generic approach
    return c;
}

auto u = make_container< std::vector< A > >(A{}, A{}, A{});

しかし、次のようにすると物足りなくなりました。

A a;
B b;
using P = std::pair< A, B >;
auto v = make_container< std::vector< P > >(P{a, b}, P{std::move(a), std::move(b)});

ここでは、コピー操作を移動操作に置き換えることで、値ごとに 1 つのコピー操作を節約したいと考えています (移動するAB、コピーするよりもはるかに安価であると仮定します) が、関数の引数の評価順序がC++ では定義されていません。私の現在の解決策は次のとおりです。

template< Container >
struct make_container
{

    template< typename ...Args >
    make_container(Args &&... args)
    {
        (c.push_back(std::forward< Args >(args)), ...);
    }

    operator Container () && { return std::move(c); }

private :

    Container c;

};

A a; B b;
using P = std::pair< A, B >;
using V = std::vector< P >;
V w = make_container< V >{P{a, b}, P{std::move(a), std::move(b)}};

コンストラクターの本体で自明でない作業を行うことは、しばしば悪い習慣と見なされますが、ここではリストの初期化の特性そのものを集中的に使用しました。

特定の観点から見ると、それは完全に間違ったアプローチですか?上記以外に、このアプローチの欠点は何ですか? 現在(C++11、C++14、C++1zで)関数引数の評価の予測可能な順序を達成するための別の手法はありますか?

4

2 に答える 2

1

より良い解決策があります:

template<class Container, std::size_t N>
inline Container make_container(typename Container::value_type (&&a)[N])
{
    return Container(std::make_move_iterator(std::begin(a)), std::make_move_iterator(std::end(a)));
}

次のように使用できます。

make_container<std::vector<A>>({A(1), A(2)})

可変個引数テンプレートは必要なくstd::initializer_list、まだリストの初期化ですが、そうではありません。今回は単純な配列であるため、そこから要素を移動できます。

元のソリューションと比較した注目すべき利点:

  • Containerパフォーマンスの向上:の ctor を直接呼び出すため、パフォーマンスが向上します (たとえばstd::vector、必要なすべてのメモリを予約できます)。
  • 評価順序の保証: リストの初期化です

デモ

于 2016-03-18T14:55:21.373 に答える
1

特定の観点から見ると、それは完全に間違ったアプローチですか?

不必要に理解するのが難しく、呼び出された関数がコピーする前に移動してしまうと、ひどく壊れる可能性があります。覚えておいて、std::move動かないでください。移動を可能にします。これ以上何もない。最終的に移動するのは、呼び出された関数です。パラメータを左から右処理すると、機能します。そうでなければ、そうではありません。

何が起こっているのかを明確にしてください。

A a; A b;
using V = std::vector< A >;
A c {a};
V v = make_container< V >{std::move(a), b, std::move(c)};
于 2016-03-18T06:51:01.830 に答える