sma*_*uck 5 c++ move-semantics c++11
转移到C++ 11后,我现在系统地在构造函数中按值传递我的字符串.但是现在,我意识到当使用构造函数体中的值时,它更容易引入错误:
class A(std::string val):
_val(std::move(val))
{
std::cout << val << std::endl; // Bug!!!
}
Run Code Online (Sandbox Code Playgroud)
我该怎么做才能减少弄错的可能性?
名称参数的目的是以某种独特的方式移出,至少在构造函数的实现中
A::A(std::string val_moved_from):
_val(std::move(val_moved_from))
{
std::cout << val_moved_from << std::endl; // Bug, but obvious
}
Run Code Online (Sandbox Code Playgroud)
然后尽早离开它们(例如在施工清单中)。
如果你有这么长的构造列表,你可能会错过val_moved_from其中的两个用途,这没有帮助。
另一种方法是编写一份解决此问题的提案。比如说,扩展 C++,以便可以通过对局部变量的操作来更改局部变量的类型或作用域,因此std::safe_move(X)两者都会从过期变量中移出X并标记X为过期变量,在其作用域的剩余部分中不再有效使用。弄清楚当变量半过期(在一个分支中过期,但在另一个分支中未过期)时该怎么办是一个有趣的问题。
因为这太疯狂了,我们可以把它当作一个库问题来攻击。在一定程度上,我们可以在运行时伪造这些技巧(类型发生变化的变量)。这很粗糙,但给出了一个想法:
template<typename T>
struct read_once : std::tr2::optional<T> {
template<typename U, typename=typename std::enable_if<std::is_convertible<U&&, T>::value>::type>
read_once( U&& u ):std::tr2::optional<T>(std::forward<U>(u)) {}
T move() && {
Assert( *this );
T retval = std::move(**this);
*this = std::tr2::none_t;
return retval;
}
// block operator*?
};
Run Code Online (Sandbox Code Playgroud)
即,编写一个只能从 via 读取的线性类型move,然后读取Asserts 或 throws。
然后修改你的构造函数:
A::A( read_once<std::string> val ):
_val( val.move() )
{
std::cout << val << std::endl; // does not compile
std::cout << val.move() << std::endl; // compiles, but asserts or throws
}
Run Code Online (Sandbox Code Playgroud)
使用转发构造函数,您可以公开一个不那么荒谬的没有read_once类型的接口,然后将构造函数转发到带有参数包装器的“安全”(可能private)版本。read_once<>
如果您的测试覆盖了所有代码路径,即使您多次从变量中进行和操作,您也会得到很好的Assert而不只是空的。std::stringmoveread_once