cpp const引用和右值引用的区别,std::move(移动语义), std::forward(完美转发)
文章目录
引言
-
string& 作为函数参数的问题
你在使用cpp过程是否遇到过使用string&作为函数参数,main调用的时候编译失败的问题?
例如下面这份简单的代码void func(string& a) {
}
int main() {
func(“12323”);
}编译器提示如下
[Error] invalid initialization of non-const reference of type ‘std::string& {aka std::basic_string&}’ from an rvalue of type 'const char*
const & 类型
上面的代码错误原因在于string&是一个引用类型,必须要有地址,而“asdfdsf”是const char*类型(是右值,可以理解为不可修改的值),无法直接转换到string&
简单的修改成如下可以编译通过
void func(const string& a) {
}
int main() {
func("12323");
}
如何理解func中的 a?
a是一种const string& 类型,调用func会先调用string的构造函数string(const char*)生成一个临时变量,命名为a,函数调用结束之后就会销毁。
&& 类型
还有一个方案,修改为右值引用类型
void func(string&& a) {
}
int main() {
func("12323");
}
三种引用的理解(左值引用 T & ; const引用 const T& ; 右值引用 T&&)
右值简单可以理解为没有明确地址的值,常见的右值有整数,浮点数,字符,字符串,例如以下
1,1.23, 2324, ‘c’,“23423535”
-
为什么需要右值引用?
c/c++默认值传递,传递函数参数过程中如果没有引用来修饰的话,会复制出一个临时变量出来,这个变量函数调用结束会销毁;而普通的引用只能接受有明确地址的变量,而右值引用接受没有明确地址的变量(例如上面列举的那些)
(代码1)
void func(string& a) { // 参数普通引用
b[0] = ‘a’;
}int main() { func("abcdefg"); // 不合法 string s; func(s); // 合法 }
(代码2)
void func(string&& a) { // 参数右值引用
a[0] = ‘a’;
}int main() { func("abcdefg"); // 合法 string s; func(s); // 不合法 }
-
何为左值引用(T&)
可以理解为一个指针,只不过c语言中的指针需要用*来取值,->来进行访问。引用简化了这个过程,让他使用起来更加的方便(和普通的类型一致)
函数参数传递引用相当于传递一个指针,可以避免不必要的构造函数。 -
何为const引用(const T&)
可以理解为const指针,const引用声明后没办法修改内部的值(只能调用非const方法),除此之外const引用支持右值来构造
string& aa = “21324”; // 不合法 ,因为"21324"没有明确的地址
const string& bb = “sdfdf”; // 合法,会找到合适的构造函数
b[0] = ‘a’; // 不合法,不可写入
cout << b[0] << endl; //合法,可以读 -
何为右值引用 (T &&)
例如普通的引用 int& a = 1234; 是编译吧不通过的,因为1234没有明确地址空间
string c;
string& s = “abcdefg”; //不合法
string& a = c; //合法,c有明确的地址
string&& b = c; //不合法,只能用那些没有明确地址的值来赋值,例如"abcdefg"
string&& d = “abcdefg”; // 合法 -
const T& 和 T&&右什么区别?
两者几乎很相似,只不过const T&受到const的限制还有语义上的区别-
const T&可以既可以接受普通的变量,又可以接受右值,但是操作起来不方便(受到const的限制),如果想要方便的操作的话,需要使用const_cast 把它转为普通的引用。
void func(const string& a) {
string& b = const_cast<string&>(a);
}int main() { func("abcdefg"); }
使用T&&可以省略去转换的过程
void func(const string& a) {
string& b = const_cast<string&>(a);
b[0] = ‘a’; //合法的
}void func(string&& a) { //a是已经被转换好的。 a[0] = 'a'; // 合法的 }
-
语义上,一般const修饰过的变量一般就认为不可修改。(虽然可以修改但最好不要这么做)
如果你想让你的函数即可接受普通的左值,又可以接受临时的变量,还想减少不必要的构造函数调用,可以这么做。
void func(string& a) {
a[0] = ‘a’;
}void func(string&& a) { func(a); // 这这里调用 void func(const string&) } int main() { func("abcdefg"); // 调用 void func(string&&) string s; func(s); // 调用void func(string&) }
-
std::move (移动语义)
待补充
std::forward(完美转发)
待补充