欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

cpp const引用和右值引用的区别,std::move(移动语义), std::forward(完美转发)

程序员文章站 2024-03-01 09:05:28
...

文章目录

引言

  • 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的限制还有语义上的区别

    1. 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'; // 合法的 
      }
      
    2. 语义上,一般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(完美转发)

待补充

相关标签: cpp