理解 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++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结

你的华为手机升级了吗? 鸿蒙NEXT多连推5.0.123版本变化颇多

《你的华为手机升级了吗?鸿蒙NEXT多连推5.0.123版本变化颇多》现在的手机系统更新可不仅仅是修修补补那么简单了,华为手机的鸿蒙系统最近可是动作频频,给用户们带来了不少惊喜... 为了让用户的使用体验变得很好,华为手机不仅发布了一系列给力的新机,还在操作系统方面进行了疯狂的发力。尤其是近期,不仅鸿蒙O

什么是 Ubuntu LTS?Ubuntu LTS和普通版本区别对比

《什么是UbuntuLTS?UbuntuLTS和普通版本区别对比》UbuntuLTS是Ubuntu操作系统的一个特殊版本,旨在提供更长时间的支持和稳定性,与常规的Ubuntu版本相比,LTS版... 如果你正打算安装 Ubuntu 系统,可能会被「LTS 版本」和「普通版本」给搞得一头雾水吧?尤其是对于刚入

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

windows端python版本管理工具pyenv-win安装使用

《windows端python版本管理工具pyenv-win安装使用》:本文主要介绍如何通过git方式下载和配置pyenv-win,包括下载、克隆仓库、配置环境变量等步骤,同时还详细介绍了如何使用... 目录pyenv-win 下载配置环境变量使用 pyenv-win 管理 python 版本一、安装 和

C++11的函数包装器std::function使用示例

《C++11的函数包装器std::function使用示例》C++11引入的std::function是最常用的函数包装器,它可以存储任何可调用对象并提供统一的调用接口,以下是关于函数包装器的详细讲解... 目录一、std::function 的基本用法1. 基本语法二、如何使用 std::function

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数