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”

int s() {
	return 1234;	
}

int main() {
	int& f = s(); // 编译失败,左值引用必须要明确的内存地址
	int&& f = s(); // 通过
	cout << f<< endl;
} 
  • 为什么需要右值引用?
    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修饰过的变量一般就认为不可修改。(虽然可以修改但最好不要这么做)
      如果你想让你的函数即可接受普通的左值,又可以接受临时的变量,还想减少不必要的构造函数调用,可以这么做。

      string& func(string& a) {
          a[0] = 's'; 
          return a;
      }
      string& func(string&& a) {
      	return func(a); // 这这里调用 void func(string&) 
      }
      
      int main() {
      	cout << func("55555") << endl; // 调用 void func(string&&)
      	string s = "6666";
      	cout << func(s) << endl; // 调用void func(string&) 
      } 
      

std::move (移动语义)

待补充

std::forward(完美转发)

待补充

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值