繁体   English   中英

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

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

我有一个使用std::vector模板参数的模板化类。 该参数可能不是默认可构造的。 我想减小向量的大小(将其切成给定的大小)。 明显地

vec.resize( reduced_size );

...不起作用,因为它需要默认构造函数。

我当然可以:

  1. 为任何使用的类型创建默认构造函数(这需要我在它可能不是一个好的设计选择时添加它)
  2. 将默认值传递给函数(界面的无用混乱)
  3. 将构造方法传递给模板(也是无用的混乱)

在写这个问题时,我注意到我可以从向量中erase元素直到最后:

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

...但是,我不确定这是否与resize一样有效。

有没有一种有效的方法可以在没有默认构造函数的情况下减小向量的大小?

C++11 解决方案是可以接受的。


编辑:似乎 MSVC 和 GCC 都将缩小调整大小作为擦除调用来实现,因此可以回答我的性能问题。

您使用erase想法是正确的途径。 为了减少混淆,我会写一个基于容器的算法:

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
}

这将与您的erase实现一样快(好吧,再进行一次分支检查)。

用:

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

在我的实现中,看起来我们有(有一些简化):

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;
}

其中_M_...是私有成员函数。 你真的想要_M_erase_at_end的效果。 我猜想编译器很难或不可能从v.resize(sz)优化_M_default_append调用,但在v.erase(iter, v.end())中相对容易注意到__last == end()并优化掉_GLIBCXX_MOVE3+ (end() - __last) 所以这里的erase()很可能比resize()更有效。

我希望大多数实现都是一个类似的故事:一些简单的if测试,然后调用一些相同的方法来调用最后元素的析构函数。

当然 - 当您调用resize ,您可以提供第二个参数传递正确类型的值,如果您使用resize来增加向量的大小,该值将(理论上)用于填充空位。 在 C++03 中,该参数的默认值为T() ,这是默认构造函数出现的地方(在 C++11 中,它们使用重载代替,因此您可以调用resize()来减小大小而无需任何进一步的困难)。

通过传递您自己的值,您可以避免使用默认构造函数。 如上所述,在 C++11 中,即使您不传递第二个参数,也不需要/使用默认的构造函数。

我怀疑与使用erase相比,这会带来任何真正的改进。 特别是,标准中的规范是(§23.3.6.3/9):

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

因此,在这种情况下, resizeerase之间似乎没有任何区别的真正原因。

暂无
暂无

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

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