栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

(P8-P12)decltype类型推导规则、返回值类型后置

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

(P8-P12)decltype类型推导规则、返回值类型后置

文章目录
    • 1.decltype
    • 2.推导规则
    • 3.decltype 的应用
    • 4.返回值类型后置

1.decltype

在某些情况下,不需要或者不能定义变量,但是希望得到某种类型,这时候就可以使用 C++11 提供的 decltype 关键字了

  • 它的作用是在编译器编译的时候推导出一个表达式的类型,语法格式如下:
decltype (表达式)
  • decltype 是 “declare type” 的缩写,意思是 “声明类型”。
  • decltype 的推导是在编译期完成的,它只是用于表达式类型的推导,并不会计算表达式的值。
    来看一组简单的例子:
int a = 10;
decltype(a) b = 99;                 // b -> int
decltype(a+3.14) c = 52.13;         // c -> double
decltype(a+b*c) d = 520.1314;       // d -> double
2.推导规则
  • 表达式为普通变量或者普通表达式或者类表达式,在这种情况下,使用 decltype 推导出的类型和表达式的类型是一致的。
#include 
#include 
using namespace std;

class Test
{
public:
    string text;
    static const int value = 110;
};

int main()
{
    int x = 99;
    const int &y = x;
    
    //变量 a 被推导为 int 类型
    decltype(x) a = x;
    
    //变量 b 被推导为 const int & 类型
	decltype(y) b = x;
	
    //变量 c 被推导为 const int 类型
    decltype(Test::value) c = 0;
	
	//变量 d 被推导为 string 类型
    Test t;
    decltype(t.text) d = "hello, world";

    return 0;
}
  • 表达式是函数调用,使用 decltype 推导出的类型和函数返回值一致。
    纯右值就是字面值(“wangji”,12315等都是字面值)

函数 func_cint () 返回的是一个纯右值(在表达式执行结束后不再存在的数据,也就是临时性的数据),对于纯右值而言,只有类类型可以携带const、volatile限定符,除此之外需要忽略掉这两个限定符,因此推导出的变量 d 的类型为 int 而不是 const int。

class Test{...};
//函数声明
int func_int();                 // 返回值为 int
int& func_int_r();              // 返回值为 int&
int&& func_int_rr();            // 返回值为 int&&

const int func_cint();          // 返回值为 const int
const int& func_cint_r();       // 返回值为 const int&,const int的左值引用
const int&& func_cint_rr();     // 返回值为 const int&&,const int的右值引用

const Test func_ctest();        // 返回值为 const Test

//decltype类型推导
int n = 100;
//变量 a 被推导为 int 类型
decltype(func_int()) a = 0;

//变量 b 被推导为 int& 类型		
decltype(func_int_r()) b = n;	

//变量 c 被推导为 int&& 类型
decltype(func_int_rr()) c = 0;	

//变量 d 被推导为 int 类型
decltype(func_cint())  d = 0;	

//变量 e 被推导为 const int & 类型
decltype(func_cint_r())  e = n;	

//变量 f 被推导为 const int && 类型
decltype(func_cint_rr()) f = 0;	

//变量 g 被推导为 const Test 类型
decltype(func_ctest()) g = Test();	
  • 表达式是一个左值(可以取地址的变量就是左值),或者被括号 ( ) 包围,使用 decltype 推导出的是表达式类型的引用(如果有 const、volatile 限定符不能忽略)。
#include 
#include 
using namespace std;

class Test
{
public:
    int num;
};

int main() {
    const Test obj;
    //带有括号的表达式
    //obj.num 为类的成员访问表达式,符合场景 1,因此 a 的类型为 int
    decltype(obj.num) a = 0;
	
	//obj.num 带有括号,符合场景 3,因此 b 的类型为 const int&。
    decltype((obj.num)) b = a;
    
    //加法表达式
    int n = 0, m = 0;
    //n+m 得到一个右值,符合场景 1,因此 c 的类型为 int
    decltype(n + m) c = 0;
    //n=n+m 得到一个左值 n,符合场景 3,因此 d 的类型为 int&
    decltype(n = n + m) d = n;
    return 0;
}
3.decltype 的应用

关于 decltype 的应用多出现在泛型编程中。比如我们编写一个类模板,在里边添加遍历容器的函数,操作如下:

#include 
using namespace std;

template 
class Container
{
public:
    void func(T& c)
    {
        for (m_it = c.begin(); m_it != c.end(); ++m_it)
        {
            cout << *m_it << " ";
        }
        cout << endl;
    }
private:
    ??? m_it;  // 这里不能确定迭代器类型
};

int main()
{
    const list lst;
    Container> obj;
    obj.func(lst);
    return 0;
}

在程序的第 17 行出了问题,关于迭代器变量一共有两种类型:只读(T::const_iterator)和读写(T::iterator),有了 decltype 就可以完美的解决这个问题了,当 T 是一个 非 const 容器得到一个 T::iterator,当 T 是一个 const 容器时就会得到一个 T::const_iterator。

  • eg:decltype(T().begin()) 这种写法在 vs2017/vs2019 下测试可用完美运行。
#include 
#include 
using namespace std;

template 
class Container
{
public:
    void func(T& c)
    {
        for (m_it = c.begin(); m_it != c.end(); ++m_it)
        {
            cout << *m_it << " ";
        }
        cout << endl;
    }
private:
	//T()表示构造一个对象
    decltype(T().begin()) m_it;  // 这里不能确定迭代器类型
};

int main()
{
	//常量容器
    const list lst{ 1,2,3,4,5,6,7,8,9 };
    Container> obj;
    obj.func(lst);
    return 0;
}
4.返回值类型后置
  • auto与decltype的结合,在泛型编程中的使用

在泛型编程中,可能需要通过参数的运算来得到返回值的类型,

  • 比如下面这个场景:
#include 
using namespace std;
// R->返回值类型, T->参数1类型, U->参数2类型
template 
R add(T t, U u)
{
    return t + u;
}

int main()
{
    int x = 520;
    double y = 13.14;
    // auto z = add(x, y);
    auto z = add(x, y);	// 简化之后的写法
    cout << "z: " << z << endl;
    return 0;
}
  • 关于返回值,从上面的代码可以推断出和表达式 t+u 的结果类型是一样的,因此可以通过通过 decltype 进行推导,

关于模板函数的参数 t 和 u 可以通过实参自动推导出来,因此在程序中就也可以不写。虽然通过上述方式问题被解决了,但是解决方案有点过于理想化,因为对于调用者来说,是不知道函数内部执行了什么样的处理动作的。

  • 因此如果要想解决这个问题就得直接在 add 函数身上做文章,先来看第一种写法:
template 
decltype(t+u) add(T t, U u)
{
    return t + u;
}
  • 问题:
    当我们在编译器中将这几行代码改出来后就直接报错了,因此 decltype 中的 t 和 u 都是函数参数,直接这样写相当于变量还没有定义就直接用上了,这时候变量还不存在,有点心急了。

  • 在C++11中增加了返回类型后置语法,说明白一点就是将decltype和auto结合起来完成返回类型的推导。
    其语法格式如下:

// 符号 -> 后边跟随的是函数返回值的类型
auto func(参数1, 参数2, ...) -> decltype(参数表达式)

通过对上述返回类型后置语法代码的分析,得到结论:auto 会追踪 decltype() 推导出的类型,因此上边的 add() 函数可以做如下的修改:

  • eg:
#include 
using namespace std;

template 
// 返回类型后置语法
auto add(T t, U u) -> decltype(t+u) 
{
    return t + u;
}

int main()
{
    int x = 520;
    double y = 13.14;
    // auto z = add(x, y);
    auto z = add(x, y);		// 简化之后的写法
    cout << "z: " << z << endl;
    return 0;
}
  • eg:为了进一步说明这个语法,我们再看一个例子:
    在这个例子中,通过 decltype 结合返回值后置语法很容易推导出来 test(t) 函数可能出现的返回值类型,并将其作用到了函数 myFunc() 上。
#include 
using namespace std;

int& test(int& i)
{
    return i;
}

double test(double& d)
{
    d = d + 100;
    return d;
}

template 
// 返回类型后置语法
auto myFunc(T& t) -> decltype(test(t))
{
    return test(t);
}

int main()
{
    int x = 520;
    double y = 13.14;
    // auto z = myFunc(x);
    auto z = myFunc(x);             // 简化之后的写法
    cout << "z: " << z << endl;

    // auto z = myFunc(y);
    auto z1 = myFunc(y);            // 简化之后的写法
    cout << "z1: " << z1 << endl;
    return 0;
}
  • 测试:

  • 参考:链接

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/931970.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号