vector resize with move赋值:结果类型必须可从输入类型构造



我想尝试添加一个移动赋值运算符到我的Mesh类下面,知道网格的矢量出现在我的Model类的字段成员:

#include <vector>
struct Mesh {
std::vector<int> vertexes;

Mesh() {
}

Mesh& operator=(Mesh&& m) {
vertexes = std::move(m.vertexes);

return *this;
}
};

struct Model {
std::vector<Mesh> meshes;

Model() {
meshes.resize(10);
}
};

int main() {
Model model;

return 0;
}

这段代码不起作用。下面是我得到的错误(clang比gcc更明确):

In file included from script.cpp:1:                                                   
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/vector:63:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:90:7: error: static_assert failed due to requirement 'is_constructib
le<Mesh, Mesh &&>::value' "result type must be constructible from input type"                                                                                                
static_assert(is_constructible<_ValueType, _Tp>::value,                                                                                                                
^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:182:4: note: in instantiation of function template specialization 's
td::__check_constructible<Mesh, Mesh &&>' requested here                                                                                                                     
= _GLIBCXX_USE_ASSIGN_FOR_INIT(_ValueType2, _From);                
^                                                                           
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:101:13: note: expanded from macro '_GLIBCXX_USE_ASSIGN_FOR_INIT'
&& std::__check_constructible<T, U>()                                                                                                                                    
^                                                                         
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:372:19: note: in instantiation of function template specialization '
std::uninitialized_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
return std::uninitialized_copy(__first, __last, __result);                                                                                                             
^                                                                   
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:396:19: note: in instantiation of function template specialization '
std::__uninitialized_copy_a<std::move_iterator<Mesh *>, Mesh *, Mesh>' requested here
return std::__uninitialized_copy_a                                                                                                                                     
^         
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/vector.tcc:674:14: note: in instantiation of function template specialization 'std::__un
initialized_move_if_noexcept_a<Mesh *, Mesh *, std::allocator<Mesh>>' requested here
std::__uninitialized_move_if_noexcept_a(                                                                                                               
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_vector.h:1011:4: note: in instantiation of member function 'std::vector<Mesh>::_M_de
fault_append' requested here                                                                                                                                                 
_M_default_append(__new_size - size());
^
script.cpp:20:12: note: in instantiation of member function 'std::vector<Mesh>::resize' requested here
meshes.resize(10);   
In file included from script.cpp:1:
In file included from /usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/vector:62:
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_construct.h:119:25: error: call to implicitly-deleted copy constructor of 'Mesh'
::new((void*)__p) _Tp(std::forward<_Args>(__args)...);
^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:120:11: note: in instantiation of function template specialization '
std::_Construct<Mesh, Mesh>' requested here 
std::_Construct(std::__addressof(*__cur), *__first);
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:137:16: note: in instantiation of function template specialization '
std::__do_uninit_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
{ return std::__do_uninit_copy(__first, __last, __result); }
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:185:2: note: in instantiation of function template specialization 's
td::__uninitialized_copy<false>::__uninit_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
__uninit_copy(__first, __last, __result);
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:372:19: note: in instantiation of function template specialization '
std::uninitialized_copy<std::move_iterator<Mesh *>, Mesh *>' requested here
return std::uninitialized_copy(__first, __last, __result);
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_uninitialized.h:396:19: note: in instantiation of function template specialization '
std::__uninitialized_copy_a<std::move_iterator<Mesh *>, Mesh *, Mesh>' requested here
return std::__uninitialized_copy_a
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/vector.tcc:674:14: note: in instantiation of function template specialization 'std::__un
initialized_move_if_noexcept_a<Mesh *, Mesh *, std::allocator<Mesh>>' requested here
std::__uninitialized_move_if_noexcept_a(
^
/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_vector.h:1011:4: note: in instantiation of member function 'std::vector<Mesh>::_M_de
fault_append' requested here
_M_default_append(__new_size - size());
^
script.cpp:20:12: note: in instantiation of member function 'std::vector<Mesh>::resize' requested here
meshes.resize(10);
^
script.cpp:9:9: note: copy constructor is implicitly deleted because 'Mesh' has a user-declared move assignment operator
Mesh& operator=(Mesh&& m) {
^

我确实意识到:

  • std::vector::resize()要求value_type是默认可构造的(因此使用空构造函数Mesh::Mesh())。
  • 移动赋值操作符(和移动构造函数一样)删除复制构造函数和复制赋值操作符。

我的问题是:

  • 为什么std::vector::resize()试图调用删除的复制构造函数为新项目分配空间,当定义空构造函数时?

  • 失败是因为std::vector::resize()的一个要求没有得到满足吗?

  • 如果是这样,我如何满足它而不删除移动分配?(如果我做我的代码工作,与resize()相同)。

std::vector::resize()要求TMoveInsertibleDefaultInsertable

/MoveInsertible:

如果Astd::allocator<T>,则调用place -new,如::new((void*)p) T(rv)(直到c++ 20)std::construct_at(p, rv)(从c++ 20起)这有效地要求T是可移动的。

如果使用std::allocator<T>或类似的分配器,类不必实现move构造函数来满足此类型要求:接受const T&参数的复制构造函数可以绑定右值表达式。如果MoveInsertable类实现了move构造函数,它也可以实现move语义,以利用构造后rv的值未指定的事实。

和per Move构造函数:

隐式声明的移动构造函数

如果没有为类类型(struct,classunion)提供用户定义的移动构造函数,和以下所有为真:

  • 没有用户声明的复制构造函数;
  • 没有用户声明的拷贝赋值操作符;
  • 没有用户声明的移动赋值操作符;
  • 没有用户声明的析构函数。

则编译器将move构造函数声明为其类的非显式inline public成员,签名为T::T(T&&)

你的Mesh类没有任何用户声明的复制/移动构造函数,但是它有一个用户声明的移动赋值操作符。因此,Mesh没有编译器生成的move构造函数,所以它不是move constructible,因此不满足MoveInsertible的要求,因此出现编译错误。

要修复这个而不删除移动赋值操作符,您需要添加一个用户声明的复制构造函数和/或移动构造函数到Mesh,例如:

struct Mesh {
std::vector<int> vertexes;

Mesh() {
}

Mesh(const Mesh& m) : vertexes(m.vertexes) {
}
// and/or:
Mesh(Mesh&& m) : vertexes(std::move(m.vertexes)) {
}
// in which case, you may as well consider adding this, too:
Mesh& operator=(const Mesh& m) {
if (this != &m) {
vertexes = m.vertexes;
}
return *this;
}
Mesh& operator=(Mesh&& m) {
vertexes = std::move(m.vertexes);
return *this;
}
};

但是,std::vector本身是完全可复制和可移动的,因此如果您可以忘记不必要的要求并完全摆脱move赋值操作符,那么所有编译器生成的构造函数和赋值操作符都可以满足此示例:

struct Mesh {
std::vector<int> vertexes;
};

你的类Mesh声明了一个显式的默认构造函数,默认情况下,删除move构造函数。

要么不声明构造函数,要么声明所有这5个函数:

  • 默认构造函数
  • 拷贝构造函数
  • <
  • 转移构造函数/gh>
  • 复制操作符
  • <
  • 移动运营商/gh>

在大多数情况下,默认值应该可以正常工作。

的例子:

#include <vector>
// Quick explanation of the rules of 0, 3 and 5
struct Mesh_rule_of_0 {
std::vector<int> vertices;
// rule of zero.  No explicit constructors, nor operator=() defined.
// The compiler will generate implicit defaults for you.
};
struct Model_0 {
std::vector<Mesh_rule_of_0> meshes;
Model_0() { meshes.resize(10); }
};
struct Mesh_rule_of_3 {
std::vector<int> vertices;
// if one of these 3 is defined, then all three must be defined
// The compiler will _try_ to generate implicit move operations for you.
Mesh_rule_of_3() = default;
Mesh_rule_of_3(const Mesh_rule_of_3&) = default;
Mesh_rule_of_3& operator=(const Mesh_rule_of_3&) = default;
};
struct Model_3 {
std::vector<Mesh_rule_of_3> meshes;
Model_3() { meshes.resize(10); }
};
struct Mesh_rule_of_5 {
std::vector<int> vertices;
// rule of 5, if a move operator is defined, all 5 must be
// defined.
Mesh_rule_of_5() = default;
Mesh_rule_of_5(const Mesh_rule_of_5&) = default;
Mesh_rule_of_5(Mesh_rule_of_5&&) = default;
Mesh_rule_of_5& operator=(const Mesh_rule_of_5&) = default;
Mesh_rule_of_5& operator=(Mesh_rule_of_5&&) = default;
};
struct Model_5 {
std::vector<Mesh_rule_of_5> meshes;
Model_5() { meshes.resize(10); }
};
struct Mesh_fail {
std::vector<int> vertices;
// this will fail, because it doesn't satisfy the requirements 
// of the rule of 0, nor of the rule of 3 nor of 
// the rule of 5
Mesh_fail() {}
Mesh_fail& operator=(Mesh_fail&&) = default;
};
// uncomment for failure
// struct Model_fail {
//     std::vector<Mesh_fail> meshes;
//     Model_fail() { meshes.resize(10); }
// };
int main() {
Model_0 m0;
Model_3 m3;
Model_5 m5;
// uncomment for failure
// Model_fail mf;
return 0;
}

最新更新