使用带有std :: fill等算法的emplace

bet*_*ido 11 c++ stl stl-algorithm c++11

我使用的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);
Run Code Online (Sandbox Code Playgroud)

但我想std::fill_n改用:

v.reserve(10);
std::fill_n(std::back_inserter(v), 10, Foo(1, 1.0));
Run Code Online (Sandbox Code Playgroud)

通过这种方式,将创建临时副本.我不知道emplace在这种情况下如何使用.我想我需要类似的东西std::back_emplacer,但我找不到这样的东西.这是C++ 11的一部分,但还没有在GCC中实现?如果它不是C++ 11的一部分,还有其他方法吗?

Luc*_*ton 13

通常使用元组来简化传递可变数量的项目(在这种情况下,参数转发到emplace_back),并使用一些技巧将元组解包回来.因此,它是可以写一个back_emplacer由要求用户利用元组工厂函数(之一效用std::make_tuple,std::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 }; }
Run Code Online (Sandbox Code Playgroud)

可以使用代码演示.在你的情况下你想要打电话std::fill_n(back_emplacer(v), 10, std::forward_as_tuple(1, 1.0));(std::make_tuple也可以接受).你还需要通常的迭代器来完成这个功能 - 我推荐使用Boost.Iterators.

但我必须强调的是,这样的实用程序在使用时不会带来太大的影响std::fill_n.在你的情况下,它将保存临时的构造Foo,有利于引用元组(如果你使用的话值元组std::make_tuple).我留给读者去寻找其他back_emplacer有用的算法.


Mat*_* M. 9

你是对的,back_emplacer标准中没有.你可以完全自己写一个,但是为了什么?

当你调用时emplace_back,你必须提供构造函数(任何构造函数)的参数:vec.emplace_back(1, 2)例如.但是,您不能随意在C++中传递参数元组,因此back_emplacer将仅限于一元构造函数.

在的情况下fill_n,您提供将参数复制,然后双方back_inserterback_emplacer会调用具有相同的参数相同的拷贝构造函数.

请注意,有构建新元素的算法generategenerate_n算法.但同样,任何临时副本都可能被省略.

因此我认为对a的需求back_emplacer相当轻,主要是因为语言不支持多个返回值.

编辑

如果你看一下下面的评论,你会认识到,使用相结合std::forward_as_tuple,并std::is_constructible可能可以写一个back_emplacer机构.感谢Luc Danton的突破.


Yak*_*ont 5

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}; });
Run Code Online (Sandbox Code Playgroud)

RVO允许将函数的返回值直接省略到将要存储的位置.

虽然逻辑上是临时创建,但实际上并没有创建临时.并且您可以访问周围范围中的所有变量来决定如何创建元素,而不仅仅是常量,如果需要的话.