[英]vector resize with move assignment: result type must be constructible from input type
I wanted to try to add a move assignment operator to my Mesh
class below, knowing that a vector of Meshes appear as a field member in my Model
class:我想尝试向下面的Mesh
class 添加一个移动赋值运算符,知道 Meshes 的向量作为字段成员出现在我的Model
class 中:
#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;
}
This code doesn't work though.但是这段代码不起作用。 Below is the error I'm getting (clang was more explicit than gcc):下面是我得到的错误(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) {
^
I do realize that:我确实意识到:
My questions are:我的问题是:
Why is std::vector::resize()
trying to call the deleted copy constructor to allocate space for new items, when an empty constructor is defined?为什么std::vector::resize()
在定义空构造函数时尝试调用已删除的复制构造函数来为新项分配空间?
Is it failing because one of std::vector::resize()
's requirements isn't met?它是否因为未满足std::vector::resize()
的要求之一而失败?
If so how do I meet it without removing the move assignment?如果是这样,我如何在不删除移动分配的情况下满足它? (if I do my code works, same with resize()). (如果我的代码有效,与 resize() 相同)。
std::vector::resize()
requires T
to be MoveInsertible and DefaultInsertable . std::vector::resize()
要求T
为MoveInsertible和DefaultInsertable 。
Per MoveInsertible :每MoveInsertible :
If
A
isstd::allocator<T>
, then this will call placement-new, as by::new((void*)p) T(rv)
(until C++20)std::construct_at(p, rv)
(since C++20) .如果A
是std::allocator<T>
,则这将调用 placement-new,如::new((void*)p) T(rv)
(C++20 前)std::construct_at(p, rv)
(C++20 起) 。 This effectively requiresT
to be move constructible.这实际上要求T
是移动可构造的。If
std::allocator<T>
or a similar allocator is used, a class does not have to implement a move constructor to satisfy this type requirement: a copy constructor that takes aconst T&
argument can bind rvalue expressions.如果使用std::allocator<T>
或类似的分配器,则class 不必实现移动构造函数来满足此类型要求:采用const T&
参数的复制构造函数可以绑定右值表达式。 If a MoveInsertable class implements a move constructor, it may also implement move semantics to take advantage of the fact that the value ofrv
after construction is unspecified.如果MoveInsertable class 实现移动构造函数,它也可以实现移动语义以利用构造后rv
的值未指定的事实。
And per Move constructors :每个Move 构造函数:
Implicitly-declared move constructor隐式声明的移动构造函数
If no user-defined move constructors are provided for a class type (
struct
,class
, orunion
), and all of the following is true :如果没有为 class 类型(struct
、class
或union
)提供用户定义的移动构造函数,并且以下所有条件都为真:
- there are no user-declared copy constructors ;没有用户声明的复制构造函数;
- there are no user-declared copy assignment operators ;没有用户声明的复制赋值运算符;
- there are no user-declared move assignment operators ;没有用户声明的移动赋值运算符;
- there is no user-declared destructor .没有用户声明的析构函数。
then the compiler will declare a move constructor as a non- explicit
inline public
member of its class with the signatureT::T(T&&)
.然后编译器会将移动构造函数声明为其 class 的非显式inline public
成员,其签名为T::T(T&&)
。
Your Mesh
class does not have any user-declared copy/move constructors, but it does have a user-declared move assignment operator.您的Mesh
class 没有任何用户声明的复制/移动构造函数,但它确实有一个用户声明的移动赋值运算符。 As such, Mesh
does not have a compiler-generated move constructor, so it is not move constructible , and thus does not satisfy the requirements of MoveInsertible , hence the compiler error.因此, Mesh
没有编译器生成的 move 构造函数,因此它不是move constructible ,因此不满足MoveInsertible的要求,因此编译器错误。
To fix this without removing your move assignment operator, as you requested, you will need to add a user-declared copy constructor and/or move constructor to Mesh
, eg:要按照您的要求在不删除移动赋值运算符的情况下解决此问题,您需要将用户声明的复制构造函数和/或移动构造函数添加到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;
}
};
However, std::vector
is fully copyable and movable by itself, so if you can forget your unnecessary requirement and get rid of the move assignment operator altogether, then all of the compiler-generated constructors and assignment operators will suffice for this example:但是, std::vector
本身是完全可复制和移动的,因此如果您可以忘记不必要的要求并完全摆脱移动赋值运算符,那么所有编译器生成的构造函数和赋值运算符都足以满足此示例:
struct Mesh {
std::vector<int> vertexes;
};
Your class Mesh declares an explicit default constructor, which by default, deletes the move constructor.您的 class Mesh 声明了一个显式默认构造函数,默认情况下会删除移动构造函数。
Either do not declare a constructor, or declare all these 5 functions:要么不声明构造函数,要么声明所有这 5 个函数:
In most cases, but not all, defaults should work fine.在大多数情况下(但不是全部),默认值应该可以正常工作。
Example:例子:
#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;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.