5

我有以下情况,我需要从 t1 移动构造 t2。不幸的是,这是不可能的(我想是违反常量的)
从 foo 的调用者那里透明地处理这个问题的正确方法是什么?(即,不需要按值传递和显式 std::move)

struct T
{
    T() = default;
    ~T() = default;
    T(T&&) = default;
};

T foo(const T& t) 
{
    T t3;

    if (predicate)
        return t3;
    else
        return std::move(t);
}

int main()
{
  T t1;
  T t2 = foo(t1);

  return 0; 
}
4

3 回答 3

6

从 foo 的调用者透明地处理该问题的正确方法是什么?

你不能那样做,这是故意的。左值永远不会透明地移动。

由于移出对象通常处于未知(但合法)状态,因此如果客户端可以将左值作为参数传递给函数,并且允许该函数静默地离开它,那将是危险的。客户端可能会在不知情的情况下留下一个僵尸对象!

因此,该语言强制执行从左值移动必须是有意识的行为的规则,并且应该通过调用将左值显式转换为右值(实际上是一个 xvalue)来实现std::move()

在这种情况下,我会说你的函数foo()——不管它实际上应该做什么——应该接受一个右值引用,并且客户端应该像这样调用它:

T t2 = foo(std::move(t1)); 

foo()请注意,一旦您将无意义的名称转换为反映某些具体程序中某些特定操作的语义的名称,最后一个建议可能不正确。但是,在不知道该操作的含义的情况下,我只能就如何编译代码片段以及如何考虑一般的移动语义提供正式的、机械的建议。

于 2013-05-25T00:41:59.787 回答
4

不需要按值传递和显式 std::move

C++ 不允许您在没有至少其中之一的情况下移动东西。这是设计使然

C++ 中运动的基本规则是不允许偶然发生。如果你有一个左值,并且你想将它传递给一些可能从它移动的函数,那么你作为调用者必须使用std::move. 这是设计使然,因此很清楚调用者的意图是要从中移动对象。

这样,当您扫描代码时,您可以查看是否不小心以不正确的方式使用了已移动的值,而无需看到除了明确使用std::move.

你想要的不是你应该想要的。

于 2013-05-25T00:44:36.010 回答
4

如果您非常想这样做,则可以这样做。要做到这一点,您foo最终将基本上重新实现std::move.

struct T
{
    T() = default;
    ~T() = default;
    T(T&&) = default;
};

template<class T> struct my_remove_reference      {typedef T type;};
template<class T> struct my_remove_reference<T&>  {typedef T type;};

// The third part of a normal `remove_reference`, but which we don't need in
// this case (though it'll all still be fine if you un-comment this).
//template<class T> struct my_remove_reference<T&&> {typedef T type;};

template <typename T>
typename my_remove_reference<T>::type &&foo(T &&t) {
    return static_cast<typename my_remove_reference<T>::type &&>(t);
}

int main()
{
  T t1;
  T t2 = foo(t1);

  return 0; 
}

现在,尽管您可以/可以这样做,但其他答案或多或少仍然正确 - 即使您可以做到,但您几乎肯定不应该这样做。如果您foo在支持移动构造的真实类上使用它(即,将真正将状态从源移动到目标),您最终会基本上破坏您的源对象,即使它仍然可见。

对您编辑的问题执行此操作,在哪里foo可以但并非总是会破坏源对象将特​​别有害。使用正确的名称可以清楚地表明foo确实从源头移动,这样做可能只是勉强合理-但是(至少对我而言)一个可能会或可能不会破坏其论点的函数似乎比一个可靠的。显然,foo与上面类似的版本只是做同样的事情std::move是没有意义的,但是一个版本也对输入进行了一些其他处理(并且,无论出于何种原因,不能与 一起使用std::move)可能,可能只是如果你真的没有其他选择的话,几乎不能接受。

于 2013-05-25T00:57:06.727 回答