在温故过程中,对于C++重载overload的函数匹配优先级有这样一个先后顺序:
g++ v4.8.1匹配规则;
完全匹配>常量匹配>升级转换>降级转换>省略号匹配
然后为了验证这个问题,我写了如下的示例代码:
#include <sal.h>
#define MYTRACE() do { std::cout << __FUNCTION__ << ",line" << __LINE__ << std::endl;}while(0)
class FnOverWrite
{
public:
void ooxx(const int& a,const int& b){ MYTRACE();}
void ooxx(const int&& a,const int&& b){MYTRACE();}
void ooxx(int a,double b){MYTRACE();}
void ooxx(double a,int b){MYTRACE();}
};
int main()
{
FnOverWrite fow;
int num1 = 3,num2 = 4;
double d3 = 12.12,d4=0.001;
// 调用左值引常用重载函数 ==》 void ooxx(const int& a,const int& b){ MYTRACE();}
fow.ooxx(num1,num2);
// 调用右值常引用重载函数 ==》void ooxx(const int&& a,const int&& b){MYTRACE();}
fow.ooxx(int(3),int(4));
fow.ooxx(num1,d3); /// 调用完全匹配 ==》 void ooxx(int a,double b){MYTRACE();}
fow.ooxx(d3,(int)d4); /// 编译器傻眼,必须显示转换 ==》void ooxx(double a,int b){MYTRACE();}
return 0;
}
那么,提个小问题,为什么fow.ooxx(int(3),int(4));
调用的是右值引用?
右值引用是C++11引入的一个新特性,主要用于实现移动语义和完美转发。它有以下优点和缺点:
在使用右值引用时,你需要注意以下几点:
#include <iostream>
#include <vector>
int main() {
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = std::move(v1); // 使用右值引用实现移动语义
std::cout << "v1.size(): " << v1.size() << std::endl; // 输出:v1.size(): 0
std::cout << "v2.size(): " << v2.size() << std::endl; // 输出:v2.size(): 5
return 0;
}
在这个例子中,std::move(v1)
返回一个右值引用,这个右值引用绑定到 v1
。然后,v2
的构造函数接收这个右值引用,并“窃取” v1
的资源,而不是复制它们。
#include <iostream>
#include <utility>
void foo(int& x) { std::cout << "lvalue\n"; }
void foo(int&& x) { std::cout << "rvalue\n"; }
template <typename T>
void bar(T&& x) {
foo(std::forward<T>(x)); // 使用右值引用实现完美转发
}
int main() {
int i = 0;
bar(i); // 输出:lvalue
bar(0); // 输出:rvalue
return 0;
}
在这个例子中,bar 函数接受一个右值引用参数,然后使用 std::forward(x) 将这个参数完美地转发给 foo 函数。
#include <iostream>
#include <utility>
template <typename T>
void swap(T& a, T& b) {
T tmp(std::move(a)); // 使用右值引用
a = std::move(b); // 使用右值引用
b = std::move(tmp); // 使用右值引用
}
int main() {
int x = 1;
int y = 2;
swap(x, y);
std::cout << "x: " << x << ", y: " << y << std::endl; // 输出:x: 2, y: 1
return 0;
}
#include <iostream>
int&& dangerous() {
int x = 123;
return std::move(x); // 错误:返回对局部变量的右值引用
}
int main() {
int&& r = dangerous();
std::cout << r << std::endl; // 未定义行为:r 引用了一个已经销毁的对象
return 0;
}
在这个例子中,dangerous 函数返回一个对局部变量 x 的右值引用。当 dangerous 函数返回后,x 就被销毁了,所以 r 引用了一个已经销毁的对象,这是未定义行为。
涉及到编译器和C++标准的版本。如果你的代码需要在旧版本的C++编译器上运行,那么你不能使用右值引用。