提问人:really 提问时间:12/23/2022 更新时间:12/24/2022 访问量:80
我可以可靠地emplace_back没有赋值运算符的类型的向量吗?
Can I reliably emplace_back in a vector of a type that does not have an assignment operator?
问:
我在 GCC、Clang 和 MSVC 中进行了一些测试,发现从不对包含的类调用赋值运算符。它仅在发生重新分配时调用 copy 或 move 构造函数。这种行为是否以某种方式得到标准的保证?emplace_back
用例是,我有一些类要按顺序存储在一个数字中,该数字只会随着时间的推移而增长,直到整个向量被破坏。我很乐意从赋值运算符中清理我的代码。
答:
是的,emplace back 将调用移动构造函数,前提是有一个可用(或就地构造):
#include <iostream>
#include <vector>
struct Pt {
Pt() = default;
Pt& operator=(Pt const& other) = delete;
Pt(Pt const&) { std::cout << "copy ctor" << std::endl; }
Pt(Pt&&){ std::cout << "move ctor" << std::endl; }
};
int main(int argc, char *argv[])
{
std::vector<Pt> v;
v.emplace_back(Pt{});
return 0;
}
应用于该类型的实际约束是使其为 MoveInsertable 和 EmplaceConstructible
评论
这些要求记录在 cppreference.com。
对于 std::vector<T>::emplace_back
:
型号要求
-
T
(容器的类型)必须满足MoveInsertable
和EmplaceConstructible
的要求。element
此外,还有一个一般要求,即类型必须是可擦除
的。std::vector
EmplaceConstructible 要求使用给定的分配器使用提供的参数来构造类型。由于您使用的是 默认分配器 ,这仅意味着该类型具有具有这些参数的可访问构造函数。std::allocator<T>
MoveInsertable 要求该类型可使用给定的分配器构造,使用 类型 的右值。因为这意味着该类型具有可访问的移动构造函数。T
std::allocator
可擦除要求使用给定的分配器可破坏类型。因为这意味着它有一个可访问的析构函数。std::allocator
就是这样(除了一些关于完整类型与不完整类型的规则)。没有提及赋值运算符的要求。
但在 C++11 之前,类型总是必须是 CopyAssignable
和 CopyConstructible
。此后,这种情况有所放松。现在,除了 Erasable
之外,要求仅取决于对向量执行的操作。
评论
emplace_back
无论如何,移动语义都是 C++11,所以关于旧 C++ 标准的观点不是有些无关紧要吗?
这是cpreference所说的:emplace_back
T
(容器的元素类型)必须满足 MoveInsertable 和 EmplaceConstructible 的要求。
对于 EmplaceConstructible,要求是 to be valid。这是无聊的约束,仅对实际放置元素有用。std::allocator_traits<A>::construct(m, p, args);
MoveInsertable 似乎是重新分配所需的概念。
对于 MoveInsertable,要求是 to be valid,其中 is type (即您的类型) 的右值表达式。std::allocator_traits<A>::construct(m, p, rv);
rv
T
构造的定义有点令人困惑,但对于大多数意图和目的,它似乎只是将参数转发给构造
函数。
换句话说,这两个概念似乎证实了唯一的约束是您需要提供:emplace_back
- 允许您就地构造的实际构造函数 (DUH)
- 重新分配时用于重定位的移动构造函数(如果缺少,则为复制)
评论