理解 decltype() 指定符(C++ 11 及以上版本)

2024-08-29 18:28

本文主要是介绍理解 decltype() 指定符(C++ 11 及以上版本),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

1.  功能

2.  语法格式

3.  理解

3.1 第一点

1.2  第二点

4.  例释


     在 C++ 编程语言中,decltype 是一个用于检查实体的声明类型或表达式的类型和值类别的关键字。该关键字在 C++11 中引入,主要用于泛型编程中,因为在泛型编程中,表达依赖于模板参数的类型通常很困难,甚至不可能。

    随着泛型编程技术在整个 20 世纪 90 年代变得越来越流行,人们认识到了类型推断机制的必要性。许多编译器供应商都实现了自己的运算符版本,通常称为 typeof,并且基于现有语言功能开发了一些功能有限的可移植实现。2002 年,Bjarne Stroustrup 提议在 C++ 语言中添加该运算符的标准化版本,并建议将其命名为“decltype”,以反映该运算符将产生表达式的“声明类型”。

    decltype 的语义(注:指在编程中的实际含义)旨在满足通用库编写者和新手程序员的需求。通常,推断出的类型与源代码中声明的对象或函数的类型完全匹配。与 sizeof运算符一样,decltype 的操作数不会被求值。

1.  功能

检查实体的声明类型或表达式的类型和值类别。

2.  语法格式

    decltype(实体(entity)) decltype(表达式(expression)):这里的实体可以是类对象,也可以是基本内置数据类型变量。

3.  理解

3.1 第一点

如果参数是未加括号的id表达式或未加括号的类成员访问表达式,则 decltype 会得出此表达式所命名实体的类型。如果不存在这样的实体,或者参数命名了一组重载函数,则程序格式不正确(C++ 11 版本)。

如果参数是一个未带括号的 id 表达式,用于命名结构化绑定,则 decltype 会产生引用类型(在结构化绑定声明的规范中描述)(C++ 17 及以上版本)。

如果参数是未带括号的 id 表达式,用于命名非类型模板参数,则 decltype 会生成模板参数的类型(如果模板参数是用占位符类型声明的,则在执行任何必要的类型推断之后)。即使实体是模板参数对象(即 const 对象),该类型也是非常量(C++ 20及以上版本)。

1.2  第二点

如果参数是任何其他类型 T 的表达式,且

(1) 如果表达式的值类别是 xvalue(一个“eXpiring”值,即“将逝值”,表示其资源可以复用的对象,例如,类对象),则 decltype 产生 T&&;

(2) 如果表达式的值类别是左值(lvalue,即可通取内存地址访问的对象),则 decltype 产生 T&;

(3) 如果表达式的值类别是 prvalue(“pure”rvalue,非左值),则 decltype 产生 T。

    如果表达式是一个返回类类型的 prvalue 的函数调用,或者是右操作数是这样的函数调用的逗号表达式,则不会为该 prvalue 引入临时对象(C++17及以下版本)。

    若表达式是除(可能带括号的)立即调用(自 C++20 起)之外的 prvalue,则不会从该 prvalue 中实现临时对象:这样的 prvalue 没有结果对象(C++17及以上版本)。

    由于没有创建临时对象,因此类型不需要完整或具有可用的析构函数,并且可以是抽象的。此规则不适用于子表达式:在 decltype(f(g())) 中,g() 必须具有完整类型,但 f() 不需要。

    请注意,如果对象的名称被括号括起来,它将被视为普通的左值表达式,因此 decltype(x) 和 decltype((x)) 通常是不同的类型。

    decltype 在声明难以或无法使用标准符号声明的类型时很有用,例如与 lambda 相关的类型或依赖于模板参数的类型。

4.  例释

#include <cassert>

#include <iostream>

#include <type_traits>

 

struct A { double x; };

const A* a;

 

decltype(a->x) y;       // y的类型double (声明类型)

decltype((a->x)) z = y; // z的类型是const double& (lvalue 表达式)

 

template<typename T, typename U>

auto add(T t, U u) -> decltype(t + u) // 返回类型取决于模板参数

                                      // C++14之后的版本返回类型可推导

{

    return t + u;

}

 

const int& getRef(const int* p) { return *p; }

static_assert(std::is_same_v<decltype(getRef), const int&(const int*)>);

auto getRefFwdBad(const int* p) { return getRef(p); }

static_assert(std::is_same_v<decltype(getRefFwdBad), int(const int*)>,

    "Just returning auto isn't perfect forwarding.");

decltype(auto) getRefFwdGood(const int* p) { return getRef(p); }

static_assert(std::is_same_v<decltype(getRefFwdGood), const int&(const int*)>,

    "Returning decltype(auto) perfectly forwards the return type.");

 

// Alternatively:

auto getRefFwdGood1(const int* p) -> decltype(getRef(p)) { return getRef(p); }

static_assert(std::is_same_v<decltype(getRefFwdGood1), const int&(const int*)>,

    "Returning decltype(return expression) also perfectly forwards the return type.");

 

int main()

{

    int i = 33;

    decltype(i) j = i * 2;

    static_assert(std::is_same_v<decltype(i), decltype(j)>);

    assert(i == 33 && 66 == j);

 

    auto f = [i](int av, int bv) -> int { return av * bv + i; };

    auto h = [i](int av, int bv) -> int { return av * bv + i; };

    static_assert(!std::is_same_v<decltype(f), decltype(h)>,

        "The type of a lambda function is unique and unnamed");

 

    decltype(f) g = f;

    std::cout << f(3, 3) << ' ' << g(3, 3) << '\n';

}

输出:42 42

这篇关于理解 decltype() 指定符(C++ 11 及以上版本)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1118609

相关文章

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

在macOS上安装jenv管理JDK版本的详细步骤

《在macOS上安装jenv管理JDK版本的详细步骤》jEnv是一个命令行工具,正如它的官网所宣称的那样,它是来让你忘记怎么配置JAVA_HOME环境变量的神队友,:本文主要介绍在macOS上安装... 目录前言安装 jenv添加 JDK 版本到 jenv切换 JDK 版本总结前言China编程在开发 Java

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

Java Spring的依赖注入理解及@Autowired用法示例详解

《JavaSpring的依赖注入理解及@Autowired用法示例详解》文章介绍了Spring依赖注入(DI)的概念、三种实现方式(构造器、Setter、字段注入),区分了@Autowired(注入... 目录一、什么是依赖注入(DI)?1. 定义2. 举个例子二、依赖注入的几种方式1. 构造器注入(Con

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

深入理解Go语言中二维切片的使用

《深入理解Go语言中二维切片的使用》本文深入讲解了Go语言中二维切片的概念与应用,用于表示矩阵、表格等二维数据结构,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧... 目录引言二维切片的基本概念定义创建二维切片二维切片的操作访问元素修改元素遍历二维切片二维切片的动态调整追加行动态

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL