简体   繁体   English

在没有默认构造函数的情况下减小 std::vector 的大小

[英]Reducing the size of a std::vector without a default constructor

I have a templated class using a std::vector of it's template argument.我有一个使用std::vector模板参数的模板化类。 The argument may not be default constructible.该参数可能不是默认可构造的。 I want to reduce the size of the vector (cut it to a given size).我想减小向量的大小(将其切成给定的大小)。 Obviously明显地

vec.resize( reduced_size );

...doesn't work as it requires a default constructor. ...不起作用,因为它需要默认构造函数。

I could of course:我当然可以:

  1. create the default constructor for any used type (which requires me to add it when it might not be a good design choice)为任何使用的类型创建默认构造函数(这需要我在它可能不是一个好的设计选择时添加它)
  2. pass a default value to the function (useless clutter of the interface)将默认值传递给函数(界面的无用混乱)
  3. pass a construction method to the template (also useless clutter)将构造方法传递给模板(也是无用的混乱)

While writing the question, I noticed that I can erase the elements from the vector up to the end:在写这个问题时,我注意到我可以从向量中erase元素直到最后:

vec.erase ( vec.begin() + position, vec.end() );

... however, I'm not sure if this will be as efficient as resize . ...但是,我不确定这是否与resize一样有效。

Is there an efficient way to reduce a vector's size without a default constructor?有没有一种有效的方法可以在没有默认构造函数的情况下减小向量的大小?

C++11 solutions are acceptable. C++11 解决方案是可以接受的。


EDIT: Seems that both MSVC and GCC implement shrinking resize as a erase call, so that answers my performance question.编辑:似乎 MSVC 和 GCC 都将缩小调整大小作为擦除调用来实现,因此可以回答我的性能问题。

Your idea to use erase is the right route.您使用erase想法是正确的途径。 To reduce the amount of confusion, I would write a container based algorithm:为了减少混淆,我会写一个基于容器的算法:

template<typename Container>
Container&& reduce_size( Container&& c, std::size_t amount ) {
  amount = std::min( amount, c.size() ); // paranoid!
  c.erase( end(c)-amount, end(c) );
  return std::forward<Container>(c); // I like my container-algorithms to pass through
}

which will be as fast as your erase implementation (well, one more branch and check).这将与您的erase实现一样快(好吧,再进行一次分支检查)。

Use:用:

std::vector< Foo > blah;
blah.emplace_back( 7 );
reduce_size( blah, 10 );

In my implementation, it looks like we have (with a few simplifications):在我的实现中,看起来我们有(有一些简化):

void std::vector<T,A>::resize(size_type __new_size)
{
    if (__new_size > size())
        _M_default_append(__new_size - size());
    else if (__new_size < size())
        _M_erase_at_end(begin() + __new_size);
}

auto std::vector<T,A>::erase(iterator __first, iterator __last) -> iterator
{
    if (__first != __last)
    {
        if (__last != end())
            _GLIBCXX_MOVE3(__last, end(), __first);
        _M_erase_at_end(__first + (end() - __last));
    }
    return __first;
}

where _M_... are private member functions.其中_M_...是私有成员函数。 You really want the effects of _M_erase_at_end .你真的想要_M_erase_at_end的效果。 I would guess it would be hard or impossible for a compiler to optimize the _M_default_append call out of v.resize(sz) , but relatively easy to notice in v.erase(iter, v.end()) that __last == end() and optimize away the _GLIBCXX_MOVE3 and the + (end() - __last) .我猜想编译器很难或不可能从v.resize(sz)优化_M_default_append调用,但在v.erase(iter, v.end())中相对容易注意到__last == end()并优化掉_GLIBCXX_MOVE3+ (end() - __last) So erase() could very well be more efficient than resize() here.所以这里的erase()很可能比resize()更有效。

I would expect most implementations to be a similar story: a few simple if tests, and then calling some identical method to call destructors for elements at the end.我希望大多数实现都是一个类似的故事:一些简单的if测试,然后调用一些相同的方法来调用最后元素的析构函数。

Sure -- when you call resize , you can supply a second parameter passing a value of the correct type that would (theoretically) be used to fill in the empty spots if you use resize to increase the size of the vector.当然 - 当您调用resize ,您可以提供第二个参数传递正确类型的值,如果您使用resize来增加向量的大小,该值将(理论上)用于填充空位。 In C++03, that argument has a default value of T() , which is where the default ctor comes into things (in C++11, they use overloading instead, so you can call resize() to reduce size without any further difficulty).在 C++03 中,该参数的默认值为T() ,这是默认构造函数出现的地方(在 C++11 中,它们使用重载代替,因此您可以调用resize()来减小大小而无需任何进一步的困难)。

By passing a value of your own, you avoid the need for the default ctor.通过传递您自己的值,您可以避免使用默认构造函数。 As noted above, in C++11, the default ctor won't be needed/used even if you don't pass the second argument.如上所述,在 C++11 中,即使您不传递第二个参数,也不需要/使用默认的构造函数。

I doubt this will give any real improvement compared to using erase though.我怀疑与使用erase相比,这会带来任何真正的改进。 In particular, the specification in the standard is (§23.3.6.3/9):特别是,标准中的规范是(§23.3.6.3/9):

If sz <= size() , equivalent to erase(begin() + sz, end());如果sz <= size() ,相当于erase(begin() + sz, end()); . .

As such, there seems to be no real reason for any difference between resize and erase in this case.因此,在这种情况下, resizeerase之间似乎没有任何区别的真正原因。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM