据我理解,在重新分配 vector 内存时,移动构造函数和移动赋值函数必须标记为 noexcept 才能被编译器利用。
然而,是否存在真实世界的情况,即移动构造或移动赋值可能会抛出异常?
更新:当类在构造时拥有已分配的资源时,不能使用不抛异常的移动。
然而,是否存在真实世界的情况,即移动构造或移动赋值可能会抛出异常?
更新:当类在构造时拥有已分配的资源时,不能使用不抛异常的移动。
#include <cassert>
#include <cstddef>
#include <iostream>
#include <vector>
template <class T>
class allocator
{
int id_;
public:
using value_type = T;
allocator(int id) noexcept : id_(id) {}
template <class U> allocator(allocator<U> const& u) noexcept : id_(u.id_) {}
value_type*
allocate(std::size_t n)
{
return static_cast<value_type*>(::operator new (n*sizeof(value_type)));
}
void
deallocate(value_type* p, std::size_t) noexcept
{
::operator delete(p);
}
template <class U, class V>
friend
bool
operator==(allocator<U> const& x, allocator<V> const& y) noexcept
{
return x.id_ == y.id_;
}
};
template <class T, class U>
bool
operator!=(allocator<T> const& x, allocator<U> const& y) noexcept
{
return !(x == y);
}
template <class T> using vector = std::vector<T, allocator<T>>;
struct A
{
static bool time_to_throw;
A() = default;
A(const A&) {if (time_to_throw) throw 1;}
A& operator=(const A&) {if (time_to_throw) throw 1; return *this;}
};
bool A::time_to_throw = false;
int
main()
{
vector<A> v1(5, A{}, allocator<A>{1});
vector<A> v2(allocator<A>{2});
v2 = std::move(v1);
try
{
A::time_to_throw = true;
v1 = std::move(v2);
assert(false);
}
catch (int i)
{
std::cout << i << '\n';
}
}
1
propagate_on_container_move_assignment::value
为false且涉及的两个分配器不相等时,vector<T, A>
移动赋值运算符会复制/移动其元素。如果其中任何一个复制/移动操作引发了异常,则容器移动赋值也将引发异常。std::list
,移动动态分配的节点也是可行的。除非您想保留被移动对象的某些“僵尸”(部分可用)状态,否则没有必要在移动时进行分配。另一个错误是程序员错误(无法分配不兼容的分配器),应该调用 std::terminate()。 - Valentin Mileapropagate_on_container_move_assignment
。这是一个方便的链接:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4527.pdf - Howard Hinnantstd::pair<T, U>
,其中 T
可以使用 noexcept 进行移动操作,而 U
仅支持复制(假设复制可能会抛出异常)。那么你可以得到一个有用的 std::pair<T, U>
移动构造函数,但这个构造函数可能会抛出异常。std::move_if_noexcept
实用程序(用于实现至少具有基本异常保证的 std::vector::resize
)。