对于您的特定情况,第二个重载是无用的。
使用只有一个重载 for 的原始代码,Load
该函数被调用用于左值和右值。
使用新代码,第一个重载是为左值调用的,第二个是为右值调用的。但是,第二个重载调用第一个。最后,调用一个或另一个的效果意味着将执行相同的操作(无论第一个重载做什么)。
因此,原代码和新代码的效果是一样的,只是第一个代码更简单。
决定一个函数是否必须按值、左值引用或右值引用来接受参数,很大程度上取决于它的作用。当您想要移动传递的参数时,您应该提供一个采用右值引用的重载。那里有几个关于移动语义的很好的参考资料,所以我不会在这里介绍。
奖金:
为了帮助我表达我的观点,请考虑这个简单的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; }
};
现在考虑这个函数:
void f(const probe& p) {
probe q(p);
// use q;
}
调用f("foo");
会产生以下输出:
ctr
copy
这里没有惊喜:我们创建了一个临时probe
传递的const char*
"foo"
. 因此是第一条输出线。然后,这个临时文件被绑定到并在里面创建p
一个副本。因此是第二条输出线。q
p
f
现在,考虑p
按值取值,即f
改为:
void f(probe p) {
// use p;
}
现在的输出f("foo");
是
ctr
在这种情况下,有些人会感到惊讶:没有副本!通常,如果您通过引用获取参数并将其复制到函数中,那么最好按值获取参数。p
在这种情况下,编译器可以直接从输入( )构造参数(在这种情况下),而不是创建一个临时文件并复制它"foo"
。有关更多信息,请参阅想要速度?按值传递。戴夫亚伯拉罕斯。
该指南有两个值得注意的例外:构造函数和赋值运算符。
考虑这个类:
struct foo {
probe p;
foo(const probe& q) : p(q) { }
};
构造函数接受一个probe
by const 引用,然后将其复制到p
. 在这种情况下,遵循上述指南不会带来任何性能改进,并且probe
无论如何都会调用 ' 的复制构造函数。但是,q
按值取值可能会产生一个重载解决问题,类似于我现在将介绍的带有赋值运算符的问题。
假设我们的类probe
有一个非抛出swap
方法。那么其赋值运算符的建议实现(暂时用 C++03 术语思考)是
probe& operator =(const probe& other) {
probe tmp(other);
swap(tmp);
return *this;
}
然后,按照上面的指导,最好这样写
probe& operator =(probe tmp) {
swap(tmp);
return *this;
}
现在进入带有右值引用和移动语义的 C++11。您决定添加一个移动赋值运算符:
probe& operator =(probe&&);
现在在临时上调用赋值运算符会产生歧义,因为两个重载都是可行的,并且没有一个优先于另一个。要解决此问题,请使用赋值运算符的原始实现(通过 const 引用获取参数)。
实际上,这个问题并不是构造函数和赋值运算符所特有的,任何函数都可能发生。(不过,您更有可能使用构造函数和赋值运算符来体验它。)例如,调用g("foo");
wheng
具有以下两个重载会引起歧义:
void g(probe);
void g(probe&&);