是否存在与`std :: move`具有相反效果的强制转换(或标准函数)

时间:2014-07-21 14:39:07

标签: c++ c++11

首先,此问题不是Function dual to std::move?Does the inverse of std::move exist?的重复。我不是要问一种机制,以防止在原本会发生的情况下移动,而是复制;相反,我正在询问一种机制,使rvalue被接受在一个将被绑定到可修改左值引用的位置。事实上,这与std::move被发明的情况完全相反(即,在一个将被绑定到(可修改的)右值参考的位置接受可修改的左值。)

在我感兴趣的情况下,不会接受右值,因为上下文需要可修改的左值引用。由于某种原因,我不太了解但愿意接受,(可修改的)右值表达式将绑定到常量左值引用(不引入额外的临时值),但它不会绑定到可修改的左值引用(gcc给我的错误消息是&#34;来自类型'A'的右值的'A&amp;'类型的非const引用的无效初始化'&#34;而clang说&#34;非常数左值引用类型&#39; A&#39;不能绑定到临时类型&#39; A&#39;&#34;&#39 ;;好奇地我不能得到这些编译器中的任何一个都承认有问题的表达式具有&#39; A&amp;&amp;&amp;&#39;,即使该表达式实际上是static_cast<A&&>(...)形式,它本身也没有引起错误)。我可以理解,通常想要在需要可修改的左值引用的位置接受rvalue表达式,因为它暗示通过该左值引用完成的任何修改都将丢失,但就像调用{ {1}}对编译器说'#34;我知道这是一个左值,它将被绑定到一个右值引用(参数),因此可能会被盗,但我知道我在做什么,这是可以的这里&#34;我想在我的情况下说&#34;我知道这是暂时的,它将被绑定到一个可修改的左值引用(参数),因此通过左值引用所做的任何更改都会被忽视,但我知道我在做什么,这里没关系&#34;。

我可以通过从rvalue初始化类型A的命名对象,然后提供需要可修改左值引用的名称来解决问题。我不认为这有任何额外的运行时间开销(无论如何都需要临时值),但是必须这样做在几个方面很尴尬:必须引入一个虚拟名称,可能不得不引入一个化合物语句只是为了保存声明,将生成rvalue的表达式与函数调用分开,它为它提供了一个参数。我的问题是,这是否可以在不引入虚拟名称的情况下完成:

  
      
  1. 是否有任何方法(例如使用强制转换)将类型A的右值表达式绑定到类型为A&amp;的可修改的左值引用。没有引入A类型的命名对象?
  2.   
  3. 如果没有,这是一个刻意的选择吗? (如果有,为什么?)如果有,是否有一个与标准提供的std::move类似的机制来促进它?
  4.   

这是一个简化的插图,我需要这样的转换。我故意删除了A的特殊构造函数,以确保错误消息不涉及编译器决定引入的临时文件。当std::move替换为A&时,所有错误都会消失。

const A&

对于那些想知道我为什么需要这个的人,这里有一个用例。我有一个函数class A { int n; public: A(int n) : n(n) {} A(const A&) = delete; // no copying A(const A&&) = delete; // no moving either int value() const { return n; } }; int f(A& x) { return x.value(); } void g() { A& aref0 = A(4); // error // exact same error with "= static_cast<A&&>(A(4))" instead of A(4) A& aref1 = static_cast<A&>(A(5)); // error // exact same error with "= static_cast<A&&>(A(5))" instead of A(5) f (A(6)); //error // exact same error with "= static_cast<A&&>(A(6))" instead of A(6) A a(7); f(a); // this works A& aref2 = a; // this works too, of course } ,其参数作为输入参数,有时也作为输出参数,用一个更专业的&#34;替换提供的值。 value(该值表示树结构,并且可能已填充一些缺少的分支);因此,该值作为可修改的左值引用传递。我还有一些全局变量保存值,有时用于为此参数提供值;这些值是不可改变的,因为它们已经完全专业化了。尽管有这种恒定的性质,我过去常常不会声明这些变量f,因为这会使它们不适合作为参数。但是它们实际上被认为是全局和永久常量,所以我想重写我的代码以使其明确,并且还避免在更改const的实现时意外发生错误的可能性(例如它可能决定在抛出异常时从其参数中移出;当参数表示将被异常销毁的局部变量时,这将是正常的,但如果它被绑定到全局&#34;常量&#将是灾难性的34)。因此,每当将其中一个全局常量传递给f时,我就决定复制一份。有一个函数f可以生成并返回这样的副本,我想把它作为copy的参数调用;唉f是一个左值,由于上述原因无法做到这一点,即使这种用法非常安全,实际上比我以前的解决方案更安全。

4 个答案:

答案 0 :(得分:7)

下面提供的功能是一个坏主意。不要使用它。它为悬挂参考提供了一条非常简单的途径。我认为需要它的代码有问题并采取相应的行动。

尽管如此,由于某种原因,我认为这是一项有趣的练习,所以我不得不表现出来。

在函数内部,其参数的名称是左值,即使参数是右值引用也是如此。您可以使用该属性将参数作为左值引用返回。

template <typename T>
constexpr T& as_lvalue(T&& t) {
    return t;
};

答案 1 :(得分:7)

最简单的解决方案是:

template<typename T>
T& force(T&& t){
   return t;
}

答案 2 :(得分:2)

完全转发您的转发功能,并且no_move

template<class T> constexpr T& no_move(T&& x) {return x;}

请确保假装你有一个左值引用而不是一个右值引用是可以的,因为这样可以避免防止temporaries与非const引用等绑定。

使用此功能的任何代码都具有接近确定性的设计缺陷。

在您的示例用例中,正确的方法是将f()的参数类型更改为const&,因此不需要此类适配器,并使用const_cast添加到已保存计算的缓存 请确保不要更改声明为mutable的对象上的非const成员。

答案 3 :(得分:2)

这似乎适用于所有情况:

template <class T>
constexpr typename std::remove_reference<T>::type& copy(T&& t) {
    return t;
};

它与std::move完全相同,只是它会返回一个左值引用。