我们以下面的方法为例:
void Asset::Load( const std::string& path )
{
// complicated method....
}
Run Code Online (Sandbox Code Playgroud)
这种方法的一般用途如下:
Asset exampleAsset;
exampleAsset.Load("image0.png");
Run Code Online (Sandbox Code Playgroud)
由于我们大多数时候都知道Path是一个临时右值,所以添加这个方法的Rvalue版本是否有意义?如果是这样,这是一个正确的实施;
void Asset::Load( const std::string& path )
{
// complicated method....
}
void Asset::Load( std::string&& path )
{
Load(path); // call the above method
}
Run Code Online (Sandbox Code Playgroud)
这是编写rvalue版本方法的正确方法吗?
Cas*_*eri 62
对于您的特定情况,第二次重载是无用的.
使用只有一个重载的原始代码,Load为lvalues和rvalues调用此函数.
使用新代码,第一个重载调用lvalues,第二个重载调用rvalues.但是,第二个重载调用第一个重载.最后,调用一个或另一个的效果意味着将执行相同的操作(无论第一次重载是什么).
因此,原始代码和新代码的效果是相同的,但第一个代码更简单.
确定函数是否必须按值,左值引用或右值引用取一个参数,在很大程度上取决于它的作用.当你想移动传递的参数时,你应该提供一个带右值引用的重载.关于移动semantincs 有几个很好的参考,所以我不会在这里介绍它.
奖金:
为了帮助我,请考虑这个简单的probe课程:
struct probe {
probe(const char* ) { std::cout << "ctr " << std::endl; }
probe(const probe& ) { std::cout << "copy" << std::endl; }
probe(probe&& ) { std::cout << "move" << std::endl; }
};
Run Code Online (Sandbox Code Playgroud)
现在考虑这个功能:
void f(const probe& p) {
probe q(p);
// use q;
}
Run Code Online (Sandbox Code Playgroud)
调用f("foo");产生以下输出:
ctr
copy
Run Code Online (Sandbox Code Playgroud)
这里没有惊喜:我们创造了一个临时的probe传递const char* "foo".因此第一条输出线.然后,这个临时势必p和副本q的p创建中f.因此第二个输出线.
现在,考虑p按价值计算,即f改为:
void f(probe p) {
// use p;
}
Run Code Online (Sandbox Code Playgroud)
输出f("foo");现在是
ctr
Run Code Online (Sandbox Code Playgroud)
在这种情况下,有些人会感到惊讶:没有副本!通常,如果您通过引用获取参数并将其复制到函数内部,那么最好按值获取参数.在这种情况下,编译器可以p直接从input("foo")构造参数(在本例中),而不是创建临时和复制它.有关更多信息,请参阅想要速度?通过价值.戴夫亚伯拉罕.
本指南有两个值得注意的例外:构造函数和赋值运算符.
考虑这个课程:
struct foo {
probe p;
foo(const probe& q) : p(q) { }
};
Run Code Online (Sandbox Code Playgroud)
构造函数采用probeby const引用,然后将其复制到p.在这种情况下,遵循上面的准则并没有带来任何性能改进,probe无论如何都会调用复制构造函数.但是,q按值获取可能会产生一个类似于赋值运算符的重载决策问题,我现在将介绍它.
假设我们的班级probe有一个非投掷swap方法.然后建议的赋值运算符的实现(暂时用C++ 03术语思考)是
probe& operator =(const probe& other) {
probe tmp(other);
swap(tmp);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
然后,根据上面的指导原则,最好这样写
probe& operator =(probe tmp) {
swap(tmp);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
现在输入带有右值引用的C++ 11并移动语义.您决定添加移动赋值运算符:
probe& operator =(probe&&);
Run Code Online (Sandbox Code Playgroud)
现在,临时调用赋值运算符会产生歧义,因为两个重载都是可行的,并且没有一个优先于另一个.要解决此问题,请使用赋值运算符的原始实现(通过const引用获取参数).
实际上,这个问题并不特定于构造函数和赋值运算符,并且可能会出现在任何函数中.(更可能的是,您将使用构造函数和赋值运算符来体验它.)例如,调用g("foo");何时g具有以下两个重载会引起歧义:
void g(probe);
void g(probe&&);
Run Code Online (Sandbox Code Playgroud)
由于我们知道大多数时候 Path 是临时右值,因此添加此方法的右值版本是否有意义?
可能不是......除非你需要在内部做一些Load()需要非常量参数的棘手事情。例如,也许您想std::move(Path)进入另一个线程。在这种情况下,使用移动语义可能有意义。
这是编写方法的右值版本的正确方法吗?
不,你应该反过来做:
void Asset::load( const std::string& path )
{
auto path_copy = path;
load(std::move(path_copy)); // call the below method
}
void Asset::load( std::string&& path )
{
// complicated method....
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
33518 次 |
| 最近记录: |