错误地使用移动的值

时间:2013-07-03 13:20:18

标签: c++ c++11 move-semantics

在转向C ++ 11后,我现在系统地在构造函数中按值传递我的字符串。但是现在,我意识到当使用构造函数体中的值时,它更容易引入错误:

class A(std::string val):
  _val(std::move(val))
{
  std::cout << val << std::endl; // Bug!!!
}

我可以做些什么来减少错误的机会?

2 个答案:

答案 0 :(得分:2)

名称参数,其目的是以某种独特的方式移动 - 至少在构造函数的实现中

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类型的不太荒谬的接口,然后将构造函数转发到“安全”(可能是private)版本,其中包含read_once<>个包装器参数。

如果您的测试涵盖了所有代码路径,那么即使您从{Assert多次std::string move,您也会得到很好的read_once而不是空{{1}} s。 1}}变量。

答案 1 :(得分:0)

“在转向C ++ 11后,我现在系统地在构造函数中按值传递我的字符串。”

对不起,我不明白为什么要这样做。与传统方法相比,这提供了哪些改进? (这基本上是防错的。)

class A(const std::string & s):
  _val(s)
{
  std::cout << s << std::endl; // no Bug!!!
  std::cout << _val << std::endl; // no Bug either !!!
}