移动哪个投掷
Move which throws?
据我了解,移动构造函数和移动赋值必须标记为no,以便编译器在向量内重新分配时利用它们。
但是,是否有任何实际案例表明移动分配,移动构造实际上可能会抛出?
更新:
例如,在构造时具有已分配资源的类不能是无抛出移动。
但是,是否有任何真实世界的情况,其中移动分配, 移动构造(或交换)实际上可能会抛出?
是的。 考虑std::list
的实现。 end
迭代器必须指向列表中的"一个元素"。 存在std::list
的实现,其中end
指向的是动态分配的节点。 甚至默认构造函数也会分配这样一个节点,以便当您调用 end()
时,有一些东西可以指向。
在这样的实现中,每个构造函数都必须分配一个节点供end()
指向...甚至是移动构造函数。 该分配可能会失败,并引发异常。
这种相同的行为可以扩展到任何基于节点的容器。
这些基于节点的容器也有一些实现会进行"短字符串"优化:它们将终端节点嵌入容器类本身中,而不是动态分配。 因此,默认构造函数(和移动构造函数)不需要分配任何内容。
如果容器的分配器propagate_on_container_move_assignment::value
为假,并且 lhs 中的分配器不等于 rhs 中的分配器,则移动分配运算符可以抛出任何container<X>
。 在这种情况下,禁止移动分配运算符将内存所有权从 rhs 转移到 lhs。 如果您使用 std::allocator
,则不会发生这种情况,因为std::allocator
的所有实例彼此相等。
更新
下面是一个符合 propagate_on_container_move_assignment::value
为 false 的情况示例。 它已经针对最新版本的VS,gcc和clang进行了测试。
#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::pair<T, U>
,其中T
是不可移动的,并且U
只能复制(假设副本可以抛出)。然后你有一个有用的std::pair<T, U>
移动构造函数,它可能会抛出。
如果需要,标准库中有一个std::move_if_noexcept
实用程序(对于至少具有基本异常保证的std::vector::resize
实现很有用)。
另请参阅 Move 构造函数和强异常保证
在具有const
数据成员也可以抛出的类上移动构造函数。查看此处了解更多信息。
- 将对象移动到std::shared_ptr
- 何时在引用或唯一指针上使用移动语义
- 如何从具有移动语义的类对象中生成共享指针
- 将shared_ptr移动到<StructA>shared_ptr<变体<结构A、结构 B>>
- C / C++ 移位/偏移/向左或向右移动位图?
- MSVC将仅移动结构参数解释为指针
- 自定义先决条件对移动分配运算符有效吗
- 返回值优化:显式移动还是隐式
- 当有分配器意识的容器被复制/移动时,反弹分配器是否被复制/移走
- 为什么复制而不是移动数据元素?
- 可以使用移动语义更改或改进此C++代码吗?
- 使lambda不可复制/不可移动
- c++在使用指针时移动语义
- 将QGraphicsItem的移动区域限制在多边形区域内
- SendInput()鼠标移动计算
- 按值 C++ 返回时进行双倍移动
- 移动二维数组中的字符
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- 安全到标准:移动会员?
- OpenVR:向视图方向移动