15

vector::emplace_backベクトルの塗りつぶし中に一時的なオブジェクトの構築を避けるために使用しました。ここに簡略化されたバージョンがあります:

class Foo {
public:
    Foo(int i, double d) : i_(i), d_(d) {}
    /* ... */
};

std::vector<Foo> v;
v.reserve(10);
for (int i = 0; i < 10; i++)
    v.emplace_back(1, 1.0);

しかし、代わりに使用したかったstd::fill_n

v.reserve(10);
std::fill_n(std::back_inserter(v), 10, Foo(1, 1.0));

ただし、この方法では、一時的なコピーが作成されます。emplaceこの状況での使い方がわかりません。のようなものが必要だと思いstd::back_emplacerますが、そのようなものが見つかりませんでした。それは C++11 の一部ですが、GCC にはまだ実装されていませんか? それが C++11 の一部でない場合、それを行う他の方法はありますか?

4

6 に答える 6

15

タプルを使用して可変数の項目 (この場合は転送先のパラメータemplace_back)を簡単に渡し、タプルをアンパックして戻すちょっとしたテクニックを使うのが一般的です。そのため、意味back_emplacerのあるタプル ファクトリ関数 ( std::make_tuplestd::tie、のいずれか) を使用するようにユーザーに要求することで、ユーティリティを作成することができます。std::forward_as_tuple

#include <type_traits>
#include <tuple>

// Reusable utilites

template<typename T>
using RemoveReference = typename std::remove_reference<T>::type;
template<typename T>
using Bare = typename std::remove_cv<RemoveReference<T>>::type;

template<typename Out, typename In>
using WithValueCategoryOf = typename std::conditional<
    std::is_lvalue_reference<In>::value
    ,  typename std::add_lvalue_reference<Out>::type
    , typename std::conditional<
        std::is_rvalue_reference<Out>::value
        , typename std::add_rvalue_reference<Out>::type
        , Out
    >::type
>::type;

template<int N, typename Tuple>
using TupleElement = WithValueCategoryOf<
    typename std::tuple_element<N, RemoveReference<Tuple>>::type
    , Tuple
>;  

// Utilities to unpack a tuple
template<int... N>
struct indices {
    using next = indices<N..., sizeof...(N)>;
};

template<int N>
struct build_indices {
    using type = typename build_indices<N - 1>::type::next;
};
template<>
struct build_indices<0> {
    using type = indices<>;
};

template<typename Tuple>
constexpr
typename build_indices<std::tuple_size<Bare<Tuple>>::value>::type
make_indices() { return {}; }

template<typename Container>
class back_emplace_iterator {
public:
    explicit back_emplace_iterator(Container& container)
        : container(&container)
    {}  

    template<
        typename Tuple
        // It's important that a member like operator= be constrained
        // in this case the constraint is delegated to emplace,
        // where it can more easily be expressed (by expanding the tuple)   
        , typename = decltype( emplace(std::declval<Tuple>(), make_indices<Tuple>()) )
    >
    back_emplace_iterator& operator=(Tuple&& tuple)
    {
        emplace(*container, std::forward<Tuple>(tuple), make_indices<Tuple>());

        return *this;
    }

    template<
        typename Tuple
        , int... Indices  
        , typename std::enable_if<
            std::is_constructible<
                typename Container::value_type
                , TupleElement<Indices, Tuple>...
            >::value
            , int
        >::type...
    >
    void emplace(Tuple&& tuple, indices<Indices...>)
    {
        using std::get;
        container->emplace_back(get<Indices>(std::forward<Tuple>(tuple))...);
    }

    // Mimic interface of std::back_insert_iterator
    back_emplace_iterator& operator*() { return *this; }
    back_emplace_iterator& operator++() { return *this; }
    back_emplace_iterator operator++(int) { return *this; }

private:
    Container* container;  
};

template<typename Container>
back_emplace_iterator<Container> back_emplacer(Container& c)
{ return back_emplace_iterator<Container> { c }; }

コードのデモが利用可能です。あなたの場合、あなたは電話したいと思いますstd::fill_n(back_emplacer(v), 10, std::forward_as_tuple(1, 1.0));std::make_tupleも受け入れられます)。また、通常のイテレータを使用して機能を完成させたい場合もあります。そのためには Boost.Iterators をお勧めします。

ただし、このようなユーティリティを .NET と一緒に使用してもあまり効果がないことを強調しておく必要がありますstd::fill_nFooあなたの場合、参照のタプル(を使用する場合は値のタプル)を優先して、一時的なの構築を保存しますstd::make_tupleback_emplacer役に立つ他のアルゴリズムを見つけるのは読者に任せます。

于 2012-08-26T16:14:52.740 に答える
10

back_emplacer標準にないというのは正しいです。自分で完璧に書くこともできますが、何のために?

を呼び出すときはemplace_back、コンストラクター (任意のコンストラクター) に引数を指定する必要がありますvec.emplace_back(1, 2)。たとえば、ただし、C++ では引数のタプルを任意に渡すことはできないため、back_emplacer単項コンストラクターに限定されます。

の場合、コピーさfill_nれる引数を指定し、次にとの両方が同じ引数で同じコピー コンストラクターを呼び出します。back_inserterback_emplacer

新しい要素を構築するgenerateとのアルゴリズムがあることに注意してください。generate_nしかし、同様に、一時的なコピーはおそらく無視されます。

back_emplacerしたがって、主に言語が複数の戻り値をサポートしていないため、 a の必要性はかなり軽いと思います。

編集

以下のコメントを見ると、組み合わせを使用しstd::forward_as_tupleてメカニズムstd::is_constructibleを記述できる可能性があることがわかります。back_emplacerブレイクスルーをもたらしてくれた Luc Danton に感謝します。

于 2012-08-26T11:42:40.223 に答える
6
class Foo {
public:
  Foo(int i, double d) : i_(i), d_(d) {}
};

std::vector<Foo> v;
v.reserve(10);
std::generate_n(std::back_inserter(v), 10, [&]()->Foo{ return {1, 1.0}; });

RVO を使用すると、関数の戻り値を格納する場所に直接省略できます。

論理的には一時が作成されますが、実際には一時は作成されません。また、周囲のスコープ内のすべての変数にアクセスして、必要に応じて、定数だけでなく要素を作成する方法を決定できます。

于 2014-11-03T18:42:05.097 に答える
2

「一時的なコピー」は作成されません。に渡した一時的なものは 1 つだけですfill_n。そして、それが各値にコピーされます。

があったとしても、back_emplacerそれを何と呼びますか? emplace関数のファミリーは、コンストラクターのパラメーターを取ります。イテレータにコピーするオブジェクトfill_nを取ります。

于 2012-08-26T11:37:50.490 に答える
0

私は最近、emplace_iteratorクラスと関連するユーティリティ関数を愚かなライブラリに提出しました。元の質問を解決し、std::tuple渡された引数の自動解凍をサポートしていると思いますoperator=

編集: 更新されたリンク: https://github.com/facebook/folly/blob/master/folly/container/Iterator.h

class Widget { Widget(int, int); };

std::vector<Widget> makeWidgets(const std::vector<int>& in) {
  std::vector<Widget> out;
  std::transform(
      in.begin(),
      in.end(),
      folly::back_emplacer(out),
      [](int i) { return folly::make_emplace_args(i, i); });
  return out;
}

folly::make_emplace_argsに似てstd::make_tupleいますが、その引数をWidgetコンストラクターに完全に転送します。(std::make_tuple同様に、追加のコピーが発生する可能性があり、左辺値と右辺値の型付けを保持しません。)この特定の例では、使用std::make_tupleしても同じ効果があります。

于 2017-05-10T17:16:02.063 に答える