为什么我不能在序列中放置多个元素?

时间:2014-03-07 11:09:51

标签: c++ c++11 sequence

C ++ 11引入了emplace函数来在序列中就地构造元素。这是insert的补充,它复制或移动元素。

但是,在insert的几个重载中,只有单个元素插入版本

iterator insert( const_iterator p, T const& x);
iterator insert( const_iterator p, T&& x );

有一个emplace版本,

template< class... Args > 
iterator emplace(const_iterator p, Args&&... x);

是否有任何理由,不允许使用n就地构建emplace元素?

虽然过载如此,

template< class... Args > 
iterator emplace(const_iterator p,size_type n,Args&&... x);

就像对应的insert

一样
iterator insert(const_iterator p,size_type n,const_reference x);

可能与其他重载冲突,将构造函数参数作为tuple,或使用某些特殊标记(如in_place_t)来消除歧义。

修改      emplace_n的建议函数vector可能具有下面给出的行为

template<class... Args>
iterator emplace_n(const_iterator p,size_type n,Args&&... x)
{
    size_type const offset = p - begin();
    if(capacity() < size()+n)
    {
        vector<T> v;
        v.reserve(size()+n);
        v.assign(make_move_iterator(begin(),make_move_iterator(end());
        swap(*this,v);
    }
    auto const sz = size();
    for(auto c = 0; c != n ; ++c)
    {
        emplace_back(x...); //can do forward only if n == 1
    }
    rotate(begin()+offset,begin()+sz,end());
    return iterator{begin() + offset};
}

2 个答案:

答案 0 :(得分:2)

问题是没有简单的方法来确定一个元素的参数何时结束以及下一个元素的参数何时开始。你可以通过tuple传递参数,就像pair的分段构造函数一样,最后得到一个像这样的辅助函数:

template<int... Is>
struct index_seq { };

template<int N, int... Is>
struct make_index_seq : make_index_seq<N - 1, N - 1, Is...> { };

template<int... Is>
struct make_index_seq<0, Is...> : index_seq<Is...> { };

template<class Cont, class Tup, int... Is>
void emplace_back_impl(Cont& c, Tup&& tup, index_seq<Is...>)
{
    using std::get;
    c.emplace_back(get<Is>(std::forward<Tup>(tup))...);
}

template<class Cont, class... Tups>
void emplace_multiple(Cont& c, Tups&&... tups)
{
    int const unpack[]{
        0, ((emplace_back_impl)(c, std::forward<Tups>(tups),
                                make_index_seq<
                                    std::tuple_size<typename std::remove_reference<Tups>::type>{}
                                >()), 0)...
    };
    static_cast<void>(unpack);
}

然后你可以使用emplace_multiple函数来放置多个元素:

std::vector<std::string> xs;
emplace_multiple(xs, std::make_tuple("Hello, world!"), std::make_tuple(10, 'a'));

请注意,这使用emplace_back。如果您没有提前预留足够的空间,使用emplace加上迭代器来标记插入位置是危险的。这是因为emplaceemplace_back可以使迭代器无效(至少对于vector s),然后该函数不知道如何将新的迭代器添加到您想要的位置。 / p>

Demo here

答案 1 :(得分:1)

让我们在给定位置调用建议的函数emplace_n以某种方式安置n元素。对于emplace,显然必须转发所有参数(迭代器除外)以构造必须放置的一个元素。现在考虑只在这个位置“安置”两个要素:

vector<X> vx;
vector<X>::const_iterator it = ... ;

vx.emplace_2(it, a1, a2, a3, a4, a5);

现在,哪个参数将用于构造第一个,构造第二个元素?库有几种组合无法轻易区分。等效的“单一阵地”可能是:

auto it2 = vx.emplace_2(it);
vx.emplace(it2, a1, a2, a3, a4, a5);

//or
auto it2 = vx.emplace_2(it, a1);
vx.emplace(it2, a2, a3, a4, a5);

//or
auto it2 = vx.emplace_2(it, a1, a2);
vx.emplace(it2, a3, a4, a5);

等。你明白了。对于n> 2,它变得更加复杂。

这就是为什么多个元素没有简单的“安置”的原因。但可以使用某种emplacer-iteratorstd::transform或类似的算法,用于手持式版本pf multiple-emplace。