为了账号安全,请及时绑定邮箱和手机立即绑定

请问在C ++ 11和Boost.Container下vector :: resize

请问在C ++ 11和Boost.Container下vector :: resize

C++
杨魅力 2019-11-03 04:04:58
我有一个C ++ 03应用程序,其中std::vector<T>类型始终用作临时缓冲区。因此,通常会使用std::vector<T>::resize()来调整它们的大小,以确保它们足够大以在使用前容纳所需的数据。该函数的C ++ 03原型实际上是:void resize(size_type n, value_type val = value_type());因此实际上在调用时resize(),通过添加适当数量的拷贝来扩大向量val。但是,通常,我只需要知道它vector足够容纳我所需的数据即可。我不需要将其初始化为任何值。复制构造新值只是浪费时间。C ++ 11可以解决(我认为):在其规范中,它分为resize()两个重载:void resize(size_type n); // value initializationvoid resize(size_type n, const value_type &val); // initialization via copy这很符合C ++的哲学:只为您想要的付出。但是,正如我指出的那样,我的应用程序不能使用C ++ 11,因此当我遇到Boost.Container库时就感到很高兴,该库在其文档中指出了对该功能的支持。具体来说,boost::container::vector<T>实际上有三个重载resize():void resize(size_type n); // value initializationvoid resize(size_type n, default_init_t); // default initializationvoid resize(size_type n, const value_type &val); // initialization via copy为了验证我是否了解所有内容,我快速进行了一次测试以验证C ++ 11 std::vector<T>和的行为boost::container::vector<T>:#include <boost/container/vector.hpp>#include <iostream>#include <vector>using namespace std;namespace bc = boost::container;template <typename VecType>void init_vec(VecType &v){    // fill v with values [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]    for (size_t i = 0; i < 10; ++i) v.push_back(i);    // chop off the end of v, which now should be [1, 2, 3, 4, 5], but the other 5 values    // should remain in memory    v.resize(5);}g++在C ++ 03模式下使用4.8.1进行编译,如下所示:g++ vectest.cc./a.out产生以下输出:std: 0 1 2 3 4 0 0 0 0 0 boost: 0 1 2 3 4 0 0 0 0 0 boost w/default: 0 1 2 3 4 5 6 7 8 9这并不奇怪。我希望C ++ 03 std::vector<T>用零初始化最后的5个元素。我什至可以说服自己为什么boost::container::vector<T>要这样做(我会假设它模仿C ++ 03模式下的C ++ 03行为)。当我明确要求默认初始化时,才得到想要的效果。但是,当我以C ++ 11模式重建时,如下所示:g++ vectest.cc -std=c++11./a.out我得到以下结果:std: 0 1 2 3 4 0 0 0 0 0 boost: 0 1 2 3 4 0 0 0 0 0 boost w/default: 0 1 2 3 4 5 6 7 8 9一模一样!这导致了我的问题:在这种情况下,我应该从三个测试中看到相同的结果,这是我的错吗?这似乎表明std::vector<T>接口更改并没有真正起任何作用,因为resize()在前两种情况下,最后一次调用中添加的5个元素仍然用零初始化。
查看完整描述

3 回答

?
HUWWW

TA贡献1874条经验 获得超12个赞

因此,实际上,在调用resize()时,通过添加适当数量的val副本来扩大向量。但是,通常,我只需要知道向量足够大即可容纳我需要的数据。我不需要将其初始化为任何值。复制构造新值只是浪费时间。


不,不是。拥有实际上没有构造的元素的容器是没有意义的。我不确定您希望看到的不是零。未指定/未初始化的元素?这不是价值初始化的意思。


如果您需要N个元素,那么您应该有N个正确构造的元素,这就是需要的std::vector::resize。值初始化将使对象初始化为零,而没有默认构造函数可调用,因此,实际上这与您想要的相反,安全性和初始化程度较低,而不是更多。


我建议你真正想要的是std::vector::reserve。


这似乎表明std::vector<T>界面更改并未真正起任何作用


它肯定会产生作用,但不是您要寻找的那个。新的resize重载是为了方便起见,因此当您只需要默认或什至值初始化时,您就不必构造自己的临时文件。这不是容器工作方式的根本变化,而是它们始终持有有效的†类型的†实例。


†有效,但如果您离开它们,则处于未指定状态!



查看完整回答
反对 回复 2019-11-04
?
慕村9548890

TA贡献1884条经验 获得超4个赞

还有就是用C ++ 11个小功能上的差异resize签名,但您的测试不会揭露它。考虑类似的测试:


#include <iostream>

#include <vector>


struct X

{

    X() {std::cout << "X()\n";}

    X(const X&) {std::cout << "X(const X&)\n";}

};


int

main()

{

    std::vector<X> v;

    v.resize(5);

}

在C ++ 03下打印:


X()

X(const X&)

X(const X&)

X(const X&)

X(const X&)

X(const X&)

但是在C ++ 11下它会打印:


X()

X()

X()

X()

X()

进行此更改的动机是为了更好地支持中的不可复制(仅移动)类型vector。在大多数情况下,包括您的情况在内,此更改都没有影响。


有一种方法可以通过使用自定义分配器(您的编译器可能支持或可能不支持)来完成C ++ 11中的目标:


#include <iostream>

#include <vector>


using namespace std;


template <class T>

class no_init_alloc

    : public std::allocator<T>

{

public:

    using std::allocator<T>::allocator;


    template <class U, class... Args> void construct(U*, Args&&...) {}

};



template <typename VecType>

void init_vec(VecType &v)

{

    // fill v with values [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

    v.resize(10);

    for (size_t i = 0; i < 10; ++i) v[i] = i;  // Note this change!!!

    // chop off the end of v, which now should be [1, 2, 3, 4, 5], but the other 5 values

    // should remain in memory

    v.resize(5);

}


template <typename VecType>

void print_vec(const char *label, VecType &v)

{

    cout << label << ": ";

    for (size_t i = 0; i < v.size(); ++i)

    {

        cout << v[i] << ' ';

    }

    cout << endl;

}


int

main()

{

    std::vector<int, no_init_alloc<int>> std_vec;

    init_vec(std_vec);

    std_vec.resize(10);

    print_vec("std", std_vec);

}

哪个应该输出:


std: 0 1 2 3 4 5 6 7 8 9 

在no_init_alloc简单地拒绝做任何初始化,这是罚款int,有一个未确定的值离开它。我不得不更改您init_vec使用赋值来初始化而不是使用构造。因此,如果您不小心,可能会很危险/令人困惑。但是,它的确避免了不必要的初始化。



查看完整回答
反对 回复 2019-11-04
  • 3 回答
  • 0 关注
  • 395 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信