首先,这个问题不是Function dual to std::move?或Does the inverse of std::move exist?的重复。我不是在询问防止移动在本应发生时进行复制的机制。相反,我在询问一种机制,使可修改的左值引用的位置接受rvalue。实际上,这与
在我的情况下,rvalue将不被接受,因为上下文需要一个可修改的左值引用。由于某种我不太理解但愿意接受的原因,(可修改的)rvalue表达式将绑定到常数左值引用(而不引入额外的临时变量),但它不会绑定到可修改的左值引用(gcc给出的错误消息是“invalid initialization of non-const reference of type ‘A&’ from an rvalue of type ‘A’ ”,而clang则表示“non-const lvalue reference to type 'A' cannot bind to a temporary of type 'A' ”;奇怪的是,我无法让这两个编译器承认所讨论的表达式具有类型“A&&”,即使该表达式实际上是
我可以通过从rvalue初始化类型为A的命名对象来解决问题,然后在需要可修改的左值引用的地方提供名称。我认为这不会增加任何额外的运行时开销(rvalue需要一个临时变量),但是必须以几种方式解决这个问题:必须引入一个虚拟名称,也许必须引入一个复合语句来保存声明,将产生rvalue的表达式与提供其参数的函数调用分开。因此,我的问题是是否可以在不引入虚拟名称的情况下完成这项工作:
对于那些想知道为什么我需要这个的人,这里有一个用例。我有一个带有参数的函数f作为输入参数,有时也作为输出参数,把提供的值替换为“更专业”的值(该值表示树形结构,并且一些缺失的分支可能已经填充了);因此将此值传递为可修改的lvalue引用。我还有一些全局变量,保存的值有时用于为此参数提供值;这些值是不可改变的,因为它们已经完全专业化。尽管如此,我过去没有声明这些变量为const,因为这样会使它们不适用于参数。但是它们确实被假定为全局和永久常量,因此我想重新编写我的代码,以使这一点明确,并避免在更改f的实现时意外出错(例如,当抛出异常时,它可能决定从其参数中移动;当参数表示将被异常销毁的局部变量时,这将是可以接受的,但如果绑定到全局“常量”上,则会导致灾难)。因此,每当将这些全局常量之一传递给f时,我决定制作一份副本。有一个函数copy,它制作并返回这样的拷贝,我想将调用它作为参数传递给f;遗憾的是,由于copy(c)是一个rvalue,因此出于上述原因无法这样做,尽管这种用法是完全安全的,实际上比我的先前解决方案更安全。
std::move
被发明的情况恰好相反(即使可修改的左值被接受到将要绑定到(可修改的)rvalue引用的位置)。在我的情况下,rvalue将不被接受,因为上下文需要一个可修改的左值引用。由于某种我不太理解但愿意接受的原因,(可修改的)rvalue表达式将绑定到常数左值引用(而不引入额外的临时变量),但它不会绑定到可修改的左值引用(gcc给出的错误消息是“invalid initialization of non-const reference of type ‘A&’ from an rvalue of type ‘A’ ”,而clang则表示“non-const lvalue reference to type 'A' cannot bind to a temporary of type 'A' ”;奇怪的是,我无法让这两个编译器承认所讨论的表达式具有类型“A&&”,即使该表达式实际上是
static_cast<A&&>(...)
这样的形式,它本身不会引发错误)。我可以理解,通常不希望在需要可修改的左值引用的位置接受rvalue表达式,因为它意味着通过该左值引用进行的任何修改都将丢失,但正如调用std::move
表示对编译器说“我知道这是一个将要绑定到rvalue引用(参数)的左值,因此可能会被窃取,但我知道我在做什么,在这里是可以的”,我想在我的情况下说“我知道这是一个将要绑定到可修改的左值引用(参数)的临时对象,因此通过左值引用进行的任何更改都将不会被注意到,但我知道我在做什么,在这里是可以的”。我可以通过从rvalue初始化类型为A的命名对象来解决问题,然后在需要可修改的左值引用的地方提供名称。我认为这不会增加任何额外的运行时开销(rvalue需要一个临时变量),但是必须以几种方式解决这个问题:必须引入一个虚拟名称,也许必须引入一个复合语句来保存声明,将产生rvalue的表达式与提供其参数的函数调用分开。因此,我的问题是是否可以在不引入虚拟名称的情况下完成这项工作:
- 是否有任何方法(例如使用强制类型转换),可以将类型为A的rvalue表达式绑定到类型为A&的可修改lvalue引用,而不引入类型为A的命名对象?
- 如果没有,这是一个故意的选择吗?(如果是,为什么?)如果有,是否提供了类似于标准中提供的
std::move
机制来促进它?
这里有一个简化的示例,我需要这样的转换。我故意删除了A的特殊构造函数,以确保错误消息不涉及编译器决定引入的临时变量。当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
}
对于那些想知道为什么我需要这个的人,这里有一个用例。我有一个带有参数的函数f作为输入参数,有时也作为输出参数,把提供的值替换为“更专业”的值(该值表示树形结构,并且一些缺失的分支可能已经填充了);因此将此值传递为可修改的lvalue引用。我还有一些全局变量,保存的值有时用于为此参数提供值;这些值是不可改变的,因为它们已经完全专业化。尽管如此,我过去没有声明这些变量为const,因为这样会使它们不适用于参数。但是它们确实被假定为全局和永久常量,因此我想重新编写我的代码,以使这一点明确,并避免在更改f的实现时意外出错(例如,当抛出异常时,它可能决定从其参数中移动;当参数表示将被异常销毁的局部变量时,这将是可以接受的,但如果绑定到全局“常量”上,则会导致灾难)。因此,每当将这些全局常量之一传递给f时,我决定制作一份副本。有一个函数copy,它制作并返回这样的拷贝,我想将调用它作为参数传递给f;遗憾的是,由于copy(c)是一个rvalue,因此出于上述原因无法这样做,尽管这种用法是完全安全的,实际上比我的先前解决方案更安全。
f
的调用者完全错误的信号。使用右值引用意味着“提供一个值,并期望它被窃取并降为无用的垃圾”。但实际上,f
通常通过其参数导出信息,通过特化其值;只有在提供那些全局常量的罕见情况下,我才知道不会有额外的信息出现。此外,采用右值引用将使我不得不在所有那些本地变量作为参数的正常情况下插入std::move
。 - Marc van Leeuwenstatic_cast
用于更改表达式的类型,但根据您引用的内容,x
和std::move(x)
的类型完全相同(一旦引用被丢弃),因此在std::move
中的转换没有改变类型,而是改变了值类别。但我可以接受这种困惑。 - Marc van Leeuwenauto_ptr<boat>
的语言显然是一种可憎的东西。 - Casey