我试图尽可能接近强异常保证,但在玩的时候std::move_if_noexcept我遇到了一些看似奇怪的行为.
尽管在布展赋值运算符在下面的类标记noexcept,该拷贝赋值运算符时与有关函数的返回值调用被调用.
struct A {
A () { /* ... */ }
A (A const&) { /* ... */ }
A& operator= (A const&) noexcept { log ("copy-assign"); return *this; }
A& operator= (A&&) noexcept { log ("move-assign"); return *this; }
static void log (char const * msg) {
std::cerr << msg << "\n";
}
};
Run Code Online (Sandbox Code Playgroud)
int main () {
A x, y;
x = std::move_if_noexcept (y); // prints "copy-assign"
}
Run Code Online (Sandbox Code Playgroud)
Fil*_*efp 15
这个名称move_if_noexcept肯定意味着只要这个操作,函数就会产生一个rvalue-referencenoexcept,并且考虑到这一点,我们很快就会意识到两件事:
T&转换,T&& 或者T const&永远不会抛出异常,那么这个函数的目的是什么?move_if_noexcept神奇地推断出使用返回值的上下文?实现(2)的答案与自然同样可怕; move_if_noexcept根本不能推断出这样的上下文(因为它不是一个心灵读者),这反过来意味着该函数必须通过一些静态规则集来发挥作用.
move_if_noexceptwill,无论调用它的上下文,有条件地返回一个rvalue-reference,具体取决于参数类型的move- 构造函数的异常规范,并且它只是在初始化对象时使用(即,不是在分配它们时) ).
template<class T>
void intended_usage () {
T first;
T second (std::move_if_noexcept (first));
}
Run Code Online (Sandbox Code Playgroud)
可能是一个更好的名字move_if_move_ctor_is_noexcept_or_the_only_option; 虽然输入有点乏味,但至少它会表达预期用途.
move_if_noexcept阅读产生的提案(n3050)std::move_if_noexcept,我们找到以下段落(强调我的):
我们建议不要
std::move(x)在这些情况下使用,从而授予编译器使用任何可用移动构造函数的std::move_if_noexcept(x)权限,这些特定操作的维护者应该使用,授予权限移动,除非它可以抛出并且类型是可复制的.除非
x是一个只移动类型,或者已知有一个非平移移动构造函数,否则该操作将回退到复制x,就好像x从未获得过移动构造函数一样.
move_if_noexcept呢?std::move_if_noexcept将有条件地将传递的左值引用转换为右值引用,除非;
// Standard Draft n4140 : [utility]p2
template<class T>
constexpr conditional_t<
!is_nothrow_move_constructible::value && is_copy_constructible<T>::value,
const T&, T&&
> move_if_noexcept (T& x) noexcept;
Run Code Online (Sandbox Code Playgroud)
这基本上意味着它只会产生一个rvalue-reference,如果它能证明它是唯一可行的替代方案,或者保证不抛出异常(通过表示noexcept).
std::move是对rvalue-reference的无条件转换,而std::move_if_noexcept取决于对象可以移动构造的方式 - 因此它只应该用于我们实际构建对象的地方,而不是在我们分配它们时.
您的代码段中的复制赋值运算符被调用,因为move_if_noexcept无法找到标记的移动构造函数noexcept,但由于它具有复制构造函数,因此该函数将生成A const&适合此类型的类型.
请注意,复制构造函数符合MoveConstructible类型,这意味着我们可以通过以下调整您的代码段move_if_noexcept返回rvalue-reference:
struct A {
A () { /* ... */ }
A (A const&) noexcept { /* ... */ }
...
};
Run Code Online (Sandbox Code Playgroud)
struct A {
A ();
A (A const&);
};
A a1;
A a2 (std::move_if_noexcept (a1)); // `A const&` => copy-constructor
Run Code Online (Sandbox Code Playgroud)
struct B {
B ();
B (B const&);
B (B&&) noexcept;
};
B b1;
B b2 (std::move_if_noexcept (b1)); // `B&&` => move-constructor
// ^ it's `noexcept`
Run Code Online (Sandbox Code Playgroud)
struct C {
C ();
C (C&&);
};
C c1;
C c2 (std::move_if_noexcept (c1)); // `C&&` => move-constructor
// ^ the only viable alternative
Run Code Online (Sandbox Code Playgroud)
struct D {
C ();
C (C const&) noexcept;
};
C c1;
C c2 (std::move_if_noexcept (c1)); // C&& => copy-constructor
// ^ can be invoked with `T&&`
Run Code Online (Sandbox Code Playgroud)