我已经开始使用C++11,现在正在系统地通过值传递字符串到我的构造函数中。但是现在我意识到,如果在构造函数体中也使用该值,那么很容易引入错误:
class A(std::string val):
_val(std::move(val))
{
std::cout << val << std::endl; // Bug!!!
}
我该怎么做才能降低出错的几率?
我已经开始使用C++11,现在正在系统地通过值传递字符串到我的构造函数中。但是现在我意识到,如果在构造函数体中也使用该值,那么很容易引入错误:
class A(std::string val):
_val(std::move(val))
{
std::cout << val << std::endl; // Bug!!!
}
我该怎么做才能降低出错的几率?
在构造函数的实现中,指定其目的是以某种独特的方式被移动的参数名称
A::A(std::string val_moved_from):
_val(std::move(val_moved_from))
{
std::cout << val_moved_from << std::endl; // Bug, but obvious
}
尽早离开那些对象(在构造列表中)。
如果您的构造列表很长,可能会错过其中两个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*?
};
请编写一个线性类型,只能通过 move
读取,读取后如果有错误则会抛出 Assert
。
然后修改你的构造函数:
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
}
read_once
,并遵循“始终将'按值复制'作为read_once
”的规则,那么当你读取它时,你会得到Assert
而不是空数据。而且哨子确实能够防止老虎靠近,你看到有老虎吗? - Yakk - Adam Nevraumontstd::move
比这个更好,因为它减少了拷贝。而且,你的回答并没有回答问题。 - milleniumbugs
是一个长度为 100,000 的字符。你的版本总是会在这里复制它:_val(s)
。但是上面的版本如果调用者对构造函数使用了 std::move
或使用一个临时 std::string
构建了 A
,则不会复制它。参见这里获取更深入的讨论。请注意,拥有 std::string&& s
和 std::string const& s
构造函数比只取 std::string s
少移动一次,但需要维护两倍的代码量。 - Yakk - Adam Nevraumont