例如:
void f(T&& t); // probably making a copy of t
void g()
{
T t;
// do something with t
f(std::move(t));
// probably something else not using "t"
}在这种情况下,void f(T const& t)是否等效,因为任何好的编译器都会产生相同的代码吗?如果这件事重要的话,我对>= VC10和>= GCC 4.6感兴趣。
编辑:
根据答案,我想详细阐述一下这个问题:
比较rvalue-reference和pass-by-value方法,很容易忘记在pass-by-value中使用std::move。编译器仍然可以检查变量是否有更多的更改,并消除不必要的副本吗?
rvalue-reference方法只使优化版本“隐式”,例如f(T()),并要求用户显式指定其他情况,如f(std::move(t)),或者在用户未完成t实例时显式复制f(T(t));。那么,在这种与优化有关的情况下,rvalue-reference方法被认为是好的吗?
发布于 2013-03-25 11:55:36
肯定不一样了。第一次,T &&只能绑定到rvalue,而T const &可以绑定到rvalue和lvalue。其次,T const &不允许任何移动优化。如果您“可能希望复制t",那么T &&允许您实际复制t,这可能更有效率。
示例:
void foo(std::string const & s) { std::string local(s); /* ... */ }
int main()
{
std::string a("hello");
foo(a);
}在此代码中,包含"hello"的字符串缓冲区必须存在两次,一次在main正文中,另一次在foo正文中。相反,如果使用rvalue引用和std::move(a),则可以“移动”相同的字符串缓冲区,并且只需要一次分配和填充。
正如@Alon所指出的,正确的成语实际上是按值传递
void foo(std::string local) { /* same as above */ }
int main()
{
std::string a("hello");
foo(std::move(a));
}发布于 2013-03-25 11:44:06
嗯,这取决于f对t做了什么,如果它创建了它的副本,那么我甚至会详细地这样做:
void f(T t) // probably making a copy of t
{
m_newT = std::move(t); // save it to a member or take the resources if it is a c'tor..
}
void g()
{
T t;
// do something with t
f(std::move(t));
// probably something else not using "t"
}然后,允许进行移动c‘’tors优化,在任何情况下都采用t‘资源,如果它被“移动”到了您的函数中,那么您甚至可以获得将它移动到函数的非副本,如果它没有被移动,那么您可能必须有一个副本。
现在,如果在后面的代码中有:
f(T());然后,在f用户不知道的情况下,自由移动优化。
注意:“在这种情况下,void ( this & t)是否等效,因为任何好的编译器都会产生相同的代码吗?”
它不是等价的,而是较少的工作,因为只有“指针”被转移,没有调用c‘’tors,既不移动,也不调用其他任何东西。
发布于 2013-03-25 13:43:27
接受一个const lvalue引用和一个rvalue引用是两件不同的事情。
相似之处:
差异:
const lvalue引用将绑定到任何东西(lvalue或rvalue)。rvalue引用将只绑定到非constrvalue-更有限。const lvalue引用时,不能修改函数中的参数。当它是一个rvalue引用时(因为它不是const),它可以被修改。让我们看一些例子:
const l值参考:void f(const T& t);1. Passing an lvalue:T t;f(t);
在这里,t是一个lvalue表达式,因为它是对象的名称。const lvalue引用可以绑定到任何东西,因此t将很高兴地通过引用传递。没有东西被复制,什么都没有被移动。
2.传递价值:
f(T());
在这里,T()是一个rvalue表达式,因为它创建了一个临时对象。同样,const lvalue引用可以绑定到任何东西,所以这是可以的。没有东西被复制,什么都没有被移动。
在这两种情况下,函数中的t都是对传入的对象的引用。它不能被引用修改为const。
1. Passing an lvalue:T t;f(t);
这将给您一个编译器错误。rvalue引用不会绑定到lvalue。
2.传递价值:
f(T());
这将很好,因为rvalue引用可以绑定到rvalue。函数中的引用t将引用由T()创建的临时对象。
现在让我们考虑一下std::move。首先要做的是:std::move实际上不会移动任何东西。这个想法是,你给它一个lvalue,然后它把它变成一个rvalue。这就是它所能做的。因此,现在,如果您的f接受一个rvalue引用,您可以这样做:
T t;
f(std::move(t));这是因为,尽管t是一个lvalue,但std::move(t)是一个rvalue。现在,rvalue引用可以绑定到它。
那么,为什么你会采取一个参考价值的论点呢?实际上,除了定义move构造函数和赋值操作符之外,您不应该经常这样做。每当定义接受rvalue引用的函数时,几乎可以肯定地希望给const lvalue引用重载。它们几乎总是成对的:
void f(const T&);
void f(T&&);为什么这对函数有用?当您给它一个lvalue (或一个const rvalue)时,第一个将被调用,而当您给它一个可修改的rvalue时,将调用第二个rvalue。接收一个rvalue通常意味着您已经得到了一个临时对象,这是个好消息,因为这意味着您可以破坏它的内部,并根据您知道它不会存在很长时间的事实来执行优化。
因此,有了这对函数,您就可以在知道得到一个临时对象时进行优化。
这对函数有一个非常常见的例子:复制和移动构造函数。它们的定义通常如下:
T::T(const T&); // Copy constructor
T::T(T&&); // Move constructor因此,移动构造函数实际上只是一个副本构造函数,在接收临时对象时对其进行了优化。
当然,传递的对象并不总是临时对象。如上面所示,您可以使用std::move将lvalue转换为rvalue。然后,它似乎是函数的临时对象。使用std::move基本上是说:“我允许您将此对象视为临时对象。”它是否真的被移走是无关紧要的。
但是,除了编写复制构造函数和移动构造函数之外,最好有充分的理由使用这一对函数。如果您正在编写一个函数,它接受一个对象,并且不管它是否是一个临时对象,它的行为都是完全相同的,那么只需使用该对象的值即可!考虑:
void f(T t);
T t;
f(t);
f(T());在对f的第一次调用中,我们传递一个lvalue。它将被复制到函数中。在对f的第二个调用中,我们传递一个rvalue。该对象将被移动到函数中。瞧-我们甚至不需要使用rvalue引用来高效地移动对象。我们只是按价值拿走了它!为什么?因为用于复制/移动的构造函数是根据表达式是lvalue还是rvalue来选择的。只需让复制/移动构造函数完成他们的工作。
至于不同的参数类型是否会导致相同的代码,这是一个完全不同的问题。编译器在“如果”规则下操作.这就意味着,只要程序按照标准指令运行,编译器就可以发出它喜欢的任何代码。因此,如果函数恰好执行相同的操作,它们可能会发出相同的代码。或者他们可能不会。但是,如果您是接受const值引用和rvalue引用的函数,则这是一个不好的迹象。
https://stackoverflow.com/questions/15614026
复制相似问题