首页 > 编程语言 >C++中的值类别与模板推导

C++中的值类别与模板推导

时间:2023-02-07 18:44:25浏览次数:66  
标签:推导 右值 左值 C++ 运算符 模板 表达式 引用

C++中表达式的值类别

C++中的表达式有两个属性,分别是值类型和值类别,本篇文章着重于表达式的值类别。C++中表达式的值类别分为以下几种

  1. 泛左值(glvalue,generalized lvalue):这种表达式决定一个对象或者函数的标识,比如变量名,函数名。
  2. 纯右值(prvalue,pure rvalue):
    1. 计算某个运算对象的内置运算符的值,这种prvalue没有结果对象。
    2. 初始化一个对象,这个结果变量可能是一个变量,一个new表达式创建的对象,由temporary materialization创建的临时对象等等。
  3. 将亡值(xvalue,expiring value):将亡值是对象的资源被标记为可以被重复使用的泛左值。
  4. 左值(lvalue):左值指的是不是将亡值的泛左值。
  5. 右值(rvalue):纯右值和将亡值统称为右值。

仅仅从定义来看会很难理解这些概念,下面例举一些例子,可以帮助更好的理解。

左值

  • 变量名,函数名或者数据成员,无视类型。无论变量是左值引用或者右值引用,由变量名称组成的表达式始终是一个左值表达式
  • 一个函数调用或者运算符重载表达式,它们的返回类型为左值引用,例如std::cout << 1
  • 内置的赋值和复合赋值运算符,例如 a = 1 ,b += 1
  • 内置的前置递增或者递减运算符,++a--a
  • 字符串字面量
  • 一个类型转换表达式,目标类型为左值引用
  • 一个函数调用或者运算符重载表达式,它的返回类型为函数的右值引用
  • 一个类型转换表达式,它的目标类型为函数的右值引用

纯右值

  • 字面量,例如42,true,nullptr。注意字符串字面量不是纯右值。
  • 一个函数调用或者运算符重载表达式,它们的返回类型为非引用类型,例如str.substring(1,2)
  • a++a--,内置的后缀递增或者递减运算符
  • a+ba-ba & ba << b,其他内置的算术运算符
  • a && ba || b,!a,内置的逻辑运算符
  • a < ba == ba > b,内置的比较运算符
  • &a,内置的取址运算符
  • 一个类型转换表达式,目标类型为非引用类型,例如static_cast<double>(x)
  • this指针
  • 标量类型的非类型模板参数
  • 一个lamada expression

将亡值

  • 一个函数调用或者函数重载表达式,它的返回类型是一个右值引用,例如std::move(x)
  • 一个类型转换表达式,它的目标类型为右值引用,例如static_cast<char&&>(r)
  • 任何指明一个临时对象的表达式,在temporary materialization

这里需要注意的是temporary materialization,任何完整类型的纯右值都可以被转换为一个相同类型的将亡值,这种转换初始化一个临时对象,通过纯右值来计算一个临时对象来作为结果对象。例如

struct S {int x};
int i = S().x;//S()纯右值被转换为将亡值

以下几种情况会造成将亡值的出现

  • 绑定一个引用到纯右值
  • 对一个类纯右值实施成员访问
  • 当从花括号初始化列表初始化一个std::initializer_list<T>对象。

将亡值表达式也会标识一个对象,毕竟它也属于泛左值这个大类里面,与左值不同的是,将亡值会将对象标记为资源可复用的对象,这在移动语义里面起着至关重要的作用。

模板函数类型推导

在理解了值类别之后,就可以开始讨论模板函数的类型推导了。整个模板的类型推导十分复杂,这里不会覆盖整个模板推导,仅仅包含模板函数的推导,而且只与引用有关。

按引用传递

考虑如下模板函数

template<typename T>
void foo(T& t)
{
    
}

这个模板函数的参数是按引用传递,所以可以传递左值表达式,需要注意的是,即使你传递的是右值引用,它仍然还是一个左值表达式,如下代码所示

int&& a = 1;
foo(a);

但是这种形式下,传右值是不可以的,因为如果要左值引用右值,它必须是一个常引用,因此,如下代码是正确的


在文章开始之前,我想先定义一个概念,对象。在不同的编程语言中,对象有着不同的解释,但是在本篇文章中,我想将对象定义为存在于内存中的,由某种类型描述的数据。例如,函数中的int变量,它位于栈中,int类型表明了对象的大小。

class Foo
{
    
};

foo(Foo());//纯右值
Foo f;
foo(std::move(f));//将亡值

引用转发

在讲右值引用之前,我想先讲引用转发,引用转发是形如以下的模板函数神声明

template<typename T>
void foo(T&& t)
{
    
}

我们用T&&形式来标识一个右值引用。右值引用的作用是,当你传递一个左值表达式时,模板类型参数会被推导为一个左值引用,例如如下代码

int a = 1;
foo(a);//因为a是一个左值表达式,所以T被推导为int&

如果传递的是一个右值,那么模板类型参数会被推导为右值表达式的值类型,例如如下代码

foo(1);//纯右值,T被推导为int
foo(std::move(1));//将亡值,T被推导为int

右值引用

右值引用非常类似于引用转发,它的形式如下

template<typename T>
void foo(const T&& t)
{
    
}

右值引用比引用转发多了一个const,而且只能传递右值表达式,因此以下的代码是不正确的

int a =1;
foo(a);//我们传递了一个左值表达式,但是foo模板函数只能接收右值表达式

标签:推导,右值,左值,C++,运算符,模板,表达式,引用
From: https://www.cnblogs.com/riasartemis/p/17099475.html

相关文章

  • c++常用知识点复盘
    、Linux内存管理机制内存全貌图:  Linux内存分为用户态和内核态两种,以32位4G的Linux内存为例进行说明,其区别如下:用户态:Ring3运行于用户态的代码......
  • 【C++复习】模板与群体数据(2)
    学习重点:容器类型内部的实现机制,顺便复习前面各章内容。容器类型的具体实现不需要特别关注(目前不需要会裸手写这么一个容器类型)1、群体/线性群体群体的概念群体是指由......
  • 【八大数据排序法】基数排序法的图形理解和案例实现 | C++
    第二十章基数排序法:::hljs-center目录第二十章基数排序法●前言●认识排序●一、基数排序法是什么?1.简要介绍2.图形理解3.算法分析●二、案例实现1.......
  • vscode vue代码模板
    {"Printtoconsole":{"prefix":"vue","body":["<!--$1-->","<template>","<divclass='$2'>$5</d......
  • c++ gstreamer使用2
    1,播放教程playbin#include<gst/gst.h>#include<stdio.h>/*Structuretocontainallourinformation,sowecanpassitaround*/typedefstruct_CustomData......
  • django1.4设置模板路径和CSS,JS,image等路径的方法
    对于DJANGO这类MVC框架来说,路径问题可以称为一个谜一样的东西,很多人因为对路径不知道如何处理而觉得MVC实在是云里雾里不知所云。本文主要解决django中关于模板路径设置、CS......
  • 二分模板以及部分二分题
    题目来源于学校oj(刷一部分比较简单的题)模板题(但在A-B数对题里面改进了模板)http://www.mangata.ltd/p/P1190#include<iostream>#include<algorithm>//含有sort函数,......
  • [C++]数据结构之堆-上滤下滤以及用于排序
    #include<iostream>usingnamespacestd;/**堆,就是一棵完全二叉树,物理存储方式是数组,一般情况下,都牺牲第一个元素arr[0],剩下的就满足了从1开始计数*若堆从1开始计数,那么......
  • PAM模板
    #include<bits/stdc++.h>usingnamespacestd;constintM=5e5+5;chars[M];intlen[M],num[M],fail[M],ch[M][26],tot=1;intget_fail(intx,inti){while(......
  • POJ2487 Farey Sequence 欧拉函数模板题
    FareySequenceDescriptionTheFareySequenceFnforanyintegernwithn>=2isthesetofirreduciblerationalnumbersa/bwith0<a<b<=nandgcd(a,b)=1......