簡體   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