理解 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++ move 的作用详解及陷阱最佳实践

《C++move的作用详解及陷阱最佳实践》文章详细介绍了C++中的`std::move`函数的作用,包括为什么需要它、它的本质、典型使用场景、以及一些常见陷阱和最佳实践,感兴趣的朋友跟随小编一起看... 目录C++ move 的作用详解一、一句话总结二、为什么需要 move?C++98/03 的痛点⚡C++

详解C++ 存储二进制数据容器的几种方法

《详解C++存储二进制数据容器的几种方法》本文主要介绍了详解C++存储二进制数据容器,包括std::vector、std::array、std::string、std::bitset和std::ve... 目录1.std::vector<uint8_t>(最常用)特点:适用场景:示例:2.std::arra

C++构造函数中explicit详解

《C++构造函数中explicit详解》explicit关键字用于修饰单参数构造函数或可以看作单参数的构造函数,阻止编译器进行隐式类型转换或拷贝初始化,本文就来介绍explicit的使用,感兴趣的可以... 目录1. 什么是explicit2. 隐式转换的问题3.explicit的使用示例基本用法多参数构造

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

C++打印 vector的几种方法小结

《C++打印vector的几种方法小结》本文介绍了C++中遍历vector的几种方法,包括使用迭代器、auto关键字、typedef、计数器以及C++11引入的范围基础循环,具有一定的参考价值,感兴... 目录1. 使用迭代器2. 使用 auto (C++11) / typedef / type alias

C++ scoped_ptr 和 unique_ptr对比分析

《C++scoped_ptr和unique_ptr对比分析》本文介绍了C++中的`scoped_ptr`和`unique_ptr`,详细比较了它们的特性、使用场景以及现代C++推荐的使用`uni... 目录1. scoped_ptr基本特性主要特点2. unique_ptr基本用法3. 主要区别对比4. u

C++11中的包装器实战案例

《C++11中的包装器实战案例》本文给大家介绍C++11中的包装器实战案例,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录引言1.std::function1.1.什么是std::function1.2.核心用法1.2.1.包装普通函数1.2.

C++多线程开发环境配置方法

《C++多线程开发环境配置方法》文章详细介绍了如何在Windows上安装MinGW-w64和VSCode,并配置环境变量和编译任务,使用VSCode创建一个C++多线程测试项目,并通过配置tasks.... 目录下载安装 MinGW-w64下载安装VS code创建测试项目配置编译任务创建 tasks.js

java创建xls文件放到指定文件夹中实现方式

《java创建xls文件放到指定文件夹中实现方式》本文介绍了如何在Java中使用ApachePOI库创建和操作Excel文件,重点是如何创建一个XLS文件并将其放置到指定文件夹中... 目录Java创建XLS文件并放到指定文件夹中步骤一:引入依赖步骤二:创建XLS文件总结Java创建XLS文件并放到指定文件

C++ 多态性实战之何时使用 virtual 和 override的问题解析

《C++多态性实战之何时使用virtual和override的问题解析》在面向对象编程中,多态是一个核心概念,很多开发者在遇到override编译错误时,不清楚是否需要将基类函数声明为virt... 目录C++ 多态性实战:何时使用 virtual 和 override?引言问题场景判断是否需要多态的三个关