关于c ++: std::vector需要constructor吗

Does std::vector<Object> reserve method need a copy constructor for Object class?

我试图在std::vector中保留一些点,但出现了一个我不理解的错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Foo{
public:    
    std::string str;
    int i;

    Foo(){
        this->str ="";
        this->i = 0;
    }

    Foo(Foo &copyFoo){
        this->str = copyFoo.str; //Or get methods if private members
        this->i = copyFoo.i;
    }

    Foo(std::string str, int i){
        this->str = str;
        this->i = i;
    }
};

int main()
{
   std::vector<Foo> fooVector;
   fooVector.reserve(20); // Error

   for(int i=0; i<20; i++){
       fooVector[i] = Foo("Test", i); // Or should I use operator new?
       // Or should I even stick to the push_back method?
   }

   return 0;
}

当然,我不能保留,这可能会起作用。但现在我对它为什么不起作用感兴趣。我添加了复制构造函数,因为它看起来可能是我当时遇到的问题。但是在添加了复制构造函数之后,它也不起作用。

错误说:

In file included from

/usr/local/gcc-4.8.1/include/c++/4.8.1/vector:62:0,

1
             from main.cpp:3: /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_construct.h: In

instantiation of 'void std::_Construct(_T1*, _Args&& ...) [with _T1 =

Foo; _Args = {Foo}]':

/usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_uninitialized.h:75:53:

required from 'static _ForwardIterator

std::__uninitialized_copy::_uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator; _ForwardIterator = Foo*; bool

_TrivialValueTypes = false]' /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_uninitialized.h:117:41:

required from '_ForwardIterator

std::uninitialized_copy(_InputIterator, _InputIterator,

_ForwardIterator) [with _InputIterator = std::move_iterator; _ForwardIterator = Foo*]' /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_uninitialized.h:258:63:

required from '_ForwardIterator

std::__uninitialized_copy_a(_InputIterator, _InputIterator,

_ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator; _ForwardIterator = Foo*; _Tp = Foo]'

/usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_vector.h:1142:29:

required from 'std::vector<_Tp, _Alloc>::pointer std::vector<_Tp,

_Alloc>::_M_allocate_and_copy(std::vector<_Tp, _Alloc>::size_type, _ForwardIterator, _ForwardIterator) [with _ForwardIterator = std::move_iterator; _Tp = Foo; _Alloc = std::allocator;

std::vector<_Tp, _Alloc>::pointer = Foo*; std::vector<_Tp,

_Alloc>::size_type = long unsigned int]' /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/vector.tcc:75:70:

required from 'void std::vector<_Tp, _Alloc>::reserve(std::vector<_Tp,

_Alloc>::size_type) [with _Tp = Foo; _Alloc = std::allocator; std::vector<_Tp, _Alloc>::size_type = long unsigned int]'

main.cpp:31:24: required from here

/usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_construct.h:75:7:

error: no matching function for call to 'Foo::Foo(Foo)'

1
2
3
 { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }

   ^ /usr/local/gcc-4.8.1/include/c++/4.8.1/bits/stl_construct.h:75:7:

note: candidates are: main.cpp:22:5: note: Foo::Foo(std::string, int)

1
2
3
4
5
6
7
8
9
10
11
 Foo(std::string str, int i){

 ^ main.cpp:22:5: note:   candidate expects 2 arguments, 1 provided main.cpp:17:5: note: Foo::Foo(Foo&amp;)

 Foo(Foo &amp;copyFoo){

 ^ main.cpp:17:5: note:   no known conversion for argument 1 from 'Foo' to 'Foo&amp;' main.cpp:12:5: note: Foo::Foo()

 Foo(){

 ^ main.cpp:12:5: note:   candidate expects 0 arguments, 1 provided

问题出在哪里?我需要初始化std:vector对象,还是只需将每个位置分配给一个对象实例?

编辑:我使用C++ 11。如果我移除复制构造函数,我会在保留方法行收到以下错误:

1
required from 'void std::_Construct(_T1*, _Args&& ---) [with _TI = Foo; _Args = {Foo&}]

这就是为什么我首先编写复制构造函数的原因。

我不想使用Resize方法,因为我希望Size方法返回包含在向量中的foo对象的实际数量,而不是我保留的数量。


1
2
3
4
5
6
7
   std::vector<Foo> fooVector;
   fooVector.reserve(20); // Error

   for(int i=0; i<20; i++){
       fooVector[i] = Foo("Test", i); // Or should I use operator new?
       // Or should I even stick to the push_back method?
   }

上面的代码是错误的。您正在访问容器中size()之外的元素。惯用的方法是在容器上执行push_back/emplace_back以实际创建对象,而不仅仅是内存:

1
2
3
   for(int i=0; i<20; i++){
       fooVector.emplace_back("Test", i);  // Alternatively 'push_back'
   }

除此之外,在C++ 11中,容器中使用的类型需要复制或移动构造函数。


变化

1
Foo(Foo &copyFoo) // bah!!! This can't make copies from temporaries

1
Foo(const Foo &copyFoo)  // now that's one good-looking copy constructor


第一个问题是,复制构造函数的参数是非常量引用的,这会阻止从临时复制。

在这种情况下,根本不需要声明复制构造函数:如果不声明,那么将隐式定义一个来复制每个成员,这就是您想要的。

如果类足够复杂,需要一个非默认的复制构造函数,那么它应该是

1
Foo(Foo const &);

请注意,不一定需要复制构造函数来将类型存储在vector中;如果不希望类可复制,则移动构造函数就足够了。

第二个问题是您正在访问向量的未初始化元素。reserve只是为许多元素保留内存,而不初始化它们;您需要在访问它们之前插入元素(使用resizeinsertpush_backemplace_back或其他东西)。


正如Luchian Grigore在他的回答中指出的,首先应该修复复制构造函数。或者只是删除它,因为默认值在这里会很好。其次,要使用现有的代码,应该使用resize而不是reserve

1
2
std::vector<Foo> fooVector;
fooVector.resize(20);

后者只保留内存,但容器的大小保持不变—在您的情况下,0,因此访问元素是非法的。另一方面,resize按你的意愿行事。

但一般来说,你甚至不应该这样做(例如这里提到的)。只需在空向量上使用push_backemplace_back来填充内容。resize将调用您无论如何都要覆盖的每个元素的默认构造函数。