(C++) 属性说明符-标准属性

文章目录

  • 前言
  • 标准属性
    • 🏷️noreturn
      • ⭐(C++11) 指示函数不返回
    • 🏷️carries_dependency
      • ⭐(C++11) 指示在函数内外传播“释放-消费” `std::memory_order` 中的依赖链
    • 🏷️deprecated
      • ⭐(C++14) 指示以此属性声明的名字或实体,允许使用但因某种 *原因* 而不鼓励使用
    • 🏷️fallthrough
      • ⭐(C++17) 指示从前一 case 标号的直落是故意的,且会警告直落的编译器不应当对此诊断
    • 🏷️nodiscard
      • ⭐(C++17/20) 鼓励编译器在返回值被丢弃时发出警告
    • 🏷️maybe_unused
      • ⭐(C++17) 抑制对于未使用实体的编译器警告,如果有
    • 🏷️likely & unlikely
      • ⭐(C++20) 指示编译器应当针对此种情况进行优化:通过某条语句的执行路径比其他任何执行路径更可能或不可能发生
    • 🏷️no_unique_address
      • ⭐(C++20) 指示一个非静态数据成员不必具有与类中的其他所有非静态数据成员都不同的地址
    • 🏷️assume(表达式)
      • ⭐(C++23) 指示 表达式 在给定的位置永远为 true
  • END

前言

cppref: 属性说明符序列(C++11 起) - cppreference.com

本文着重讲解属性说明符的标准属性

基础语法见:(C++) 属性说明符-语法

属性说明符是一种与具体编译器强绑定的内容。有的属性可以作为编译器的提示,有的则是直接作为设置。

在C++11之前,不同编译器拥有不同的语法,不同的说明符。在编写跨平台程序时非常麻烦,移植性一直不是很好。

到了C++11标准终于规定了属性说明符的标准。但是所支持的说明符随着标准的提出还不是很多。但该语法仍然允许使用编译器自身的说明符,为以后的扩展做了一定的预留。

标准属性

🏷️noreturn

⭐(C++11) 指示函数不返回

指示函数不返回。

void fun_0() {
}

[[noreturn]] void fun_1() {
}

int main() {
}

编译选项:-std=c++11 -O1

对应汇编 ,通过观察可以发现,没有标注[[noreturn]] 的函数还会会有对应的ret汇编。而标注了属性的函数则直接优化掉了。

fun_0():
        ret
fun_1():
main:
        movl    $0, %eax
        ret

🏷️carries_dependency

⭐(C++11) 指示在函数内外传播“释放-消费” std::memory_order 中的依赖链

指示释放消费 std::memory_order 中的依赖链传进和传出该函数,这允许编译器跳过不必要的内存栅栏指令。

对于本标签需要了解指令内存序的知识。

简单的说在多任务,多线程中为了保证资源内存访问的逻辑正确性,在生成对应的底层代码时会生成内存栅栏指令来维护正确性。

而这些生成的指令,肯定会消耗运行的效率。特别是在一些其实没必要保护的地方,就是多余的操作,该标签就是对应优化这种情况。

#include <atomic>
#include <cassert>
#include <iostream>

void print_0(int* val) {
    std::cout << __LINE__ << " " << *val << std::endl;
}

void print_1(int* val [[carries_dependency]]) {
    std::cout << __LINE__ << " " << *val << std::endl;
}

int main() {
    int x{8172252};

    // 注意,不同编译器
    // 不同cpp标准的情况下
    // 使用等号初始化和直接初始化之间的兼容性
    std::atomic<int*> p{&x};
    // 直接等号是最强的内存序seq_cst,这里选用较弱的consume
    int* local = p.load(std::memory_order_consume);
    assert(local);

    /**
     * 依赖是明确的,因而编译器已知 local 被解引用,
     * 且它必须保证保留依赖链,以避免(某些架构上的)一个内存栅栏。
     */
    std::cout << __LINE__ << " " << *local << std::endl;

    /**
     * print_0 的定义不透明(假定它未被内联),
     * 因而编译器必须发出一个内存栅栏,
     * 以确保在 print_0 中读取 *p 返回正确值。
     */
    print_0(local);

    /**
     * 编译器可以假定,虽然 print_1 也不透明,
     * 但指令流中仍保留了从形参到解引用值的依赖,
     * 且不需要(某些架构上的)内存栅栏。
     *
     * 显然,print_1 的定义必须确实保留这项依赖,因而此属性也会影响
     * 为 print_1 所生成的代码。
     */
    print_1(local);
}

本代码的汇编比较长,不直接展示了:对应汇编

🏷️deprecated

⭐(C++14) 指示以此属性声明的名字或实体,允许使用但因某种 原因 而不鼓励使用

指示声明有此属性的名字或实体被弃用,即允许但因故不鼓励使用。

deprecated 可以在使用时添加字符串字面量来辅助编程。

#include <iostream>

void fun_no_deprecated() {
    std::cout << __func__ << std::endl;
}

[[deprecated]]
void fun_0_deprecated() {
    std::cout << __func__ << std::endl;
}

[[deprecated("Use NeogenePeriod() instead.")]]
void fun_1_deprecated() {
    std::cout << __func__ << std::endl;
}

int main() {
    fun_no_deprecated();
    fun_0_deprecated();
    fun_1_deprecated();
}

在部分编译器,或者开启部分选项后可能会直接无法完成编译:

下面选用 gcc version 11.2.0 (x86_64-posix-seh-rev3, Built by MinGW-W64 project)的运行程序的输出:

main.cpp: In function 'int main()':
main.cpp:19:21: warning: 'void fun_0_deprecated()' is deprecated [-Wdeprecated-declarations]
   19 |     fun_0_deprecated();
      |     ~~~~~~~~~~~~~~~~^~
main.cpp:8:6: note: declared here
    8 | void fun_0_deprecated() {
      |      ^~~~~~~~~~~~~~~~
main.cpp:20:21: warning: 'void fun_1_deprecated()' is deprecated: Use NeogenePeriod() instead. [-Wdeprecated-declarations]
   20 |     fun_1_deprecated();
      |     ~~~~~~~~~~~~~~~~^~
main.cpp:13:6: note: declared here
   13 | void fun_1_deprecated() {
      |      ^~~~~~~~~~~~~~~~
fun_no_deprecated
fun_0_deprecated
fun_1_deprecated

🏷️fallthrough

⭐(C++17) 指示从前一 case 标号的直落是故意的,且会警告直落的编译器不应当对此诊断

指示从前一标号直落是有意的,而在发生直落时给出警告的编译器不应诊断它。

介绍一个switch的黑科技:(C/C++) 效率黑科技-Duff’s Device_duff device

void test(int n) {
    int cnt = 0;
    
    switch (n) {
    case 1:
    case 2:  // 直落时不警告
        cnt = 2;
    case 3:  // 编译器可在发生直落时警告
        cnt = 3;
        [[fallthrough]];  // 良构
    case 4:
        if (n < 3) {
            cnt = 4;
            [[fallthrough]];  // 良构
        } else {
            return;
        }
    case 5:
        while (false) {
            [[fallthrough]];  // 非良构:下一语句不是同一迭代的一部分
        }
    case 6:
        [[fallthrough]];  // 非良构:没有后继的 case 或 default 标号
    }
}

🏷️nodiscard

⭐(C++17/20) 鼓励编译器在返回值被丢弃时发出警告

若从并非转型到 void 的弃值表达式中,调用声明为 nodiscard 的函数,或调用按值返回声明为 nodiscard 的枚举或类的函数,则鼓励编译器发布警告。

/**
 * 给自定义类型指定
 */
struct [[nodiscard]] Node {};

Node test_self_type() {
    return {};
}

/**
 * (C++20 起)
 * nodiscard(字符串字面量)
 */
[[nodiscard("please do not discard")]]
int test_fun(int x, int y) {
    return x + y;
}

int main() {
    // 虽然函数没标注
    // 但是因为返回值是一个标注了的类型
    // 因此可能有警告
    test_self_type();

    // 可能有警告
    test_fun(1, 2);
    // 正常code
    int x = test_fun(3, 4);
    return x;
}

🏷️maybe_unused

⭐(C++17) 抑制对于未使用实体的编译器警告,如果有

抑制针对未使用实体的警告。

此属性可出现在下列实体的声明中:

  • class/struct/union:struct [[maybe_unused]] S;
  • typedef,包括别名声明:[[maybe_unused]] typedef S* PS;using PS [[maybe_unused]] = S*;
  • 变量,包括静态数据成员:[[maybe_unused]] int x;
  • 非静态数据成员:union U { [[maybe_unused]] int n; };
  • 函数:[[maybe_unused]] void f();
  • 枚举:enum [[maybe_unused]] E {};
  • 枚举项:enum { A [[maybe_unused]], B [[maybe_unused]] = 42 };
  • 结构化绑定:[[maybe_unused]] auto [a, b] = std::make_pair(42, 0.23);

对于声明为 [[maybe_unused]] 的实体,如果没有使用这些实体或它们的结构化绑定,那么编译器针对未使用实体发布的警告会被抑制。

#include <cassert>

/**
 * class/struct/union
 */
struct [[maybe_unused]] S {
    [[maybe_unused]] int x;
};

/**
 * enum
 */
enum [[maybe_unused]] E { A [[maybe_unused]], B [[maybe_unused]] = 42 };

/**
 * typedef,包括别名声明
 */
using Task [[maybe_unused]] = void (*)(void);

/**
 * 未使用函数 “f”
 * 未使用参数 “thing1” 与 “thing2”
 */
[[maybe_unused]]
void f([[maybe_unused]] bool thing1, [[maybe_unused]] bool thing2) {
/**
 * 未使用标签 “lb”
 */
[[maybe_unused]] lb:

    /**
     * 发行模式中,assert 在编译中被去掉,因而未使用 “b”
     * 无警告,因为它被声明为 [[maybe_unused]]
     */
    [[maybe_unused]] bool b = thing1 && thing2;
    assert(b);
}

int main() {
}

🏷️likely & unlikely

⭐(C++20) 指示编译器应当针对此种情况进行优化:通过某条语句的执行路径比其他任何执行路径更可能或不可能发生

允许编译器为包含该语句的执行路径,比任何其他不包含该语句的执行路径,更可能或更不可能的情况进行优化。

using ll = long long;

ll binPow(ll base, ll expo, ll mod) {
    ll ans = 1;
    base %= mod;
    if (expo == 0) [[unlikely]] {
        ans = 1 % mod;
    } else [[likely]] {
        while (expo) {
            if (expo & 1) {
                ans = ans * base % mod;
            }
            base = base * base % mod;
            expo >>= 1;
        }
    }
    return ans;
};

#include <iostream>

int main() {
    std::cout << binPow(2, 100, 1e9 + 7);
}

🏷️no_unique_address

⭐(C++20) 指示一个非静态数据成员不必具有与类中的其他所有非静态数据成员都不同的地址

允许此数据成员与其类的其他非静态数据成员或基类子对象重叠。

注意,一切以编译器实际情况为准!!!

#include <cstdint>
#include <iostream>

#ifdef _MSC_VER
#define no_unique_address msvc::no_unique_address
#endif  // _MSC_VER

/**
 * 空类
 * 任何空类类型对象的大小至少为 1
 */
struct Empty {};

/**
 * 按照默认方式对齐
 * 至少需要多一个字节以给 e 唯一地址
 */
void test_1() {
    struct X {
        int32_t i;
        Empty   e;
    };

    std::cout << "size = " << sizeof(X) << '\n';
}

/***
 * 优化掉空成员
 * 和前面的内存共用
 */
void test_2() {
    struct Y {
        int32_t                     i;
        [[no_unique_address]] Empty e;
    };
    std::cout << "size = " << sizeof(Y) << '\n';
}

/**
 * e1 与 e2 不能共享同一地址,
 * 因为它们拥有相同类型,
 * 尽管它们标记有 [[no_unique_address]]。
 * 然而,其中一者可以与 c 共享地址。
 */
void test_3() {
    struct Z {
        uint8_t                     c;
        [[no_unique_address]] Empty e1, e2;
    };

    std::cout << "size = " << sizeof(Z) << '\n';
}

/**
 * e1 与 e2 不能拥有同一地址,
 * 但它们之一能与 c[0] 共享,
 * 而另一者与 c[1] 共享
 */
void test_4() {
    struct W {
        uint8_t                     c[2];
        [[no_unique_address]] Empty e1, e2;
    };
    std::cout << "size = " << sizeof(W) << '\n';
}

int main() {
    // 任何空类类型对象的大小至少为 1
    static_assert(sizeof(Empty) >= 1);

    test_1();
    test_2();
    test_3();
    test_4();
}

🏷️assume(表达式)

⭐(C++23) 指示 表达式 在给定的位置永远为 true

指示表达式在给定位置总是求值为 true。

简单说,如果assume被通过为true,则下文的程序将可能被优化。否则行为未定义。

特别注意该表达式不会求值(但它依然会潜在求值)。

#include <cmath>

void g(int) {
}
void h() {
}

void f(int& x, int y) {
    /**
     * 编译器可以假设 x 是正数
     * 可以生成更高效的代码
     */
    [[assume(x > 0)]];
    g(x / 2);

    x     = 3;
    int z = x;

    /**
     * 编译器可以假设在调用 h 后 x 的值保持相同
     * 该假设本身不会调用 h
     */
    [[assume((h(), x == z))]];
    h();
    g(x);  // 编译器可以用 g(3); 替换该语句

    /**
     * 这里没有 assume
     * 或者说上面已经作用完上一个assume了
     * 编译器不能用 g(3); 替换该语句假设只在它出现的地方适用
     */
    h();
    g(x);

    /**
     * @brief
     * 编译器可以假设 g(z) 会返回
     * 根据上面和下面的假设,编译器可以用 g(10); 替换该语句
     */
    z = std::abs(y);
    [[assume((g(z), true))]];
    g(z);

    /**
     * 这里 y != -10 的情况下行为未定义
     */
    [[assume(y == -10)]];

    /**
     * 编译器可以用 g(5); 替换该语句
     */
    [[assume((x - 1) * 3 == 12)]];
    g(x);
}

int main() {
}

目前所支持的编译器还很少(目前仅gcc13, msvc19): 测试效果

END

关注我,学习更多C/C++,算法,计算机知识

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/497713.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

GPT:多轮对话并搭建简单的聊天机器人

1 多轮对话 多轮对话能力至关重要&#xff0c;它不仅能深化交流&#xff0c;精准捕捉对方意图&#xff0c;还能促进有效沟通&#xff0c;增强理解。在智能客服、教育辅导等领域&#xff0c;多轮对话更是提升服务质量、增强用户体验的关键。 注意&#xff1a;大模型没有多轮对话…

如何在 Oracle 中使用 CREATE SEQUENCE 语句

在本文中&#xff0c;我们将讨论 Oracle CREATE SEQUENCE 语句&#xff0c;其主要目的是提供一种可靠的方法来生成唯一且连续的数值&#xff0c;通常用于数据库表中的主键字段。此功能对于维护数据完整性和效率、确保不同记录之间的标识符有序分配尤其重要。从本质上讲&#xf…

STM32G473之flash存储结构汇总

STM32G4系列单片机&#xff0c;为32位的微控制器&#xff0c;理论上其内部寄存器地址最多支持4GB的命名及查找&#xff08;2的32次方&#xff0c;地址命名为0x00000000至0xFFFFFFFF&#xff09;。STM32官方对4GB的地址存储进行编号时&#xff0c;又分割成了8个block区域&#x…

【python】网络编程socket TCP UDP

文章目录 socket常用方法TCP客户端服务器UDP客户端服务器网络编程就是实现两台计算机的通信 互联网协议族 即通用标准协议,任何私有网络只要支持这个协议,就可以接入互联网。 socket socke模块的socket()函数 import socketsock = socket.socket(Address Family, type)参…

SQLyog连接MySQL8.0+报错:错误码2058的解决方案

最近把mysql从5.7迁移到8.3.0发现连接不上 因为 MySQL 从 8.0 版本开始&#xff0c;新增了caching_sha2_password授权插件 技术博客 http://idea.coderyj.com/ 1.更换sqlyog 更新到13.1.3之后的版本 2.取消mysql8的加密授权机制 mysql> ALTER USER sqlyog% IDENTIFIED WIT…

ArcGIS制作风向频率玫瑰图

风玫瑰图是气象科学专业统计图表,用来统计某个地区一段时期内风向、风速发生频率,又分为“风向玫瑰图”和“风速玫瑰图” ;因图形似玫瑰花朵,故名。风玫瑰图对于涉及城市规划、环保、风力发电等领域有着重要的意义。风玫瑰图能够直观的显现某地区不同方位风向的频率特征,进…

边缘计算与云计算总结

一. EdgeGallery 简介 MEC场景下的EdgeGallery是让资源边缘化&#xff0c;实时完成移动网络边缘的业务处理&#xff0c;MEC场景下的EdgeGallery让开发者能更便捷地使用 5G 网络能力&#xff0c;让5G能力在边缘触手可及。 EdgeGallery是由华为、信通院、中国移动、中国联通、…

最优算法100例之11-和为S的两个数字

专栏主页:计算机专业基础知识总结(适用于期末复习考研刷题求职面试)系列文章https://blog.csdn.net/seeker1994/category_12585732.html 题目描述 输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个…

从小白-入门-进阶-高阶,四个阶段详细讲解单片机学习路线!

大家好&#xff0c;今天给大家介绍从小白-入门-进阶-高阶&#xff0c;四个阶段详细讲解单片机学习路线&#xff01;&#xff0c;文章末尾附有分享大家一个资料包&#xff0c;差不多150多G。里面学习内容、面经、项目都比较新也比较全&#xff01;可进群免费领取。 单片机学习路…

Jackson 2.x 系列【6】注解大全篇二

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 注解大全2.11 JsonValue2.12 JsonKey2.13 JsonAnySetter2.14 JsonAnyGetter2.15 …

【进程控制】进程程序替换的原理以及exec函数族

文章目录 替换原理exec函数族解释函数名解释参数 替换原理 在Linux中&#xff0c;进程的程序替换&#xff08;Process Program Replacement&#xff09;是指一个正在运行的进程使用exec函数族系统调用来加载并执行另一个程序的过程。这个新程序将替换掉原先正在执行的程序&…

VR全景赋能智慧农业,打造沉浸式种植体验平台

随着人口的增长&#xff0c;传统农业也正在面临着不一样的挑战&#xff0c;加上很多人对农业的固有印象&#xff0c;很少有年轻人愿意下到农田里&#xff0c;那么该如何提高产量、降低成本以及引导年轻人深刻感受现代农业成为了急需解决的问题。 随着城市化脚步的推进&#xff…

Codeforces Round 841 (Div. 2) C. Even Subarrays

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back #define fi first #define se second #define lson p << 1 #define rson p << 1 | 1 const int maxn 1e6 5, inf 1e9, maxm 4e4 5; co…

Tomcat配置https

前言&#xff1a;本文内容为实操记录&#xff0c;仅供参考&#xff01; 一、证书 CA证书申请下载不赘述了。 二、上传证书 进入tomcat根目录&#xff0c;conf同级目录下创建cert文件夹&#xff0c;并将证书两个文件上传到该文件夹&#xff1b; 三、编辑conf/server.xml文件 ① …

0 决策树基础

目录 1 绪论 2 模型 3 决策树面试总结 1 绪论 决策树算法包括ID3、C4.5以及C5.0等&#xff0c;这些算法容易理解&#xff0c;适用各种数据&#xff0c;在解决各种问题时都有良好表现&#xff0c;尤其是以树模型为核心的各种集成算法&#xff0c;在各个行业和领域都有广泛的…

火车头通过关键词采集文章的原理

随着互联网信息的爆炸式增长&#xff0c;网站管理员和内容创作者需要不断更新和发布新的文章&#xff0c;以吸引更多的用户和提升网站的排名。而火车头作为一款智能文章采集工具&#xff0c;在这一过程中发挥着重要作用。本文将探讨火车头如何通过关键词采集文章&#xff0c;以…

新能源汽车驱动电机振动噪音分析

驱动电机示例图 驱动电机的噪声主要分为空气动力噪声、电磁噪声和机械噪声。其中在高速运转时空气动力噪声是主要噪声&#xff0c;中低速运转时电磁噪声为主要噪声。 1、空气动力噪声&#xff1a; 空气噪声主要由于风扇转动&#xff0c;使空气流动、撞击、摩擦而产生&#x…

109、Recent Advances in 3D Gaussian Splatting

简介 论文 对3D Gaussian Splatting的综述 质量提升 Mip-Splatting观察到&#xff0c;改变采样率&#xff0c;例如焦距&#xff0c;可以通过引入高频高斯类形伪影或强膨胀效应&#xff0c;极大地影响渲染图像的质量&#xff0c;因此Mip-Splatting将3D表示的频率限制在训练图…

win10微软拼音输入法 - bug - 在PATH变量为空的情况下,无法输入中文

文章目录 win10微软拼音输入法 - bug - 在PATH变量为空的情况下&#xff0c;无法输入中文概述笔记实验前提条件100%可以重现 - 无法使用win10拼音输入法输入中文替代的输入法软件备注END win10微软拼音输入法 - bug - 在PATH变量为空的情况下&#xff0c;无法输入中文 概述 在…

【Leetcode每日一题】模拟 - 提莫攻击(难度⭐)(42)

1. 题目解析 题目链接&#xff1a;495. 提莫攻击 2.算法原理 一、分情况讨论 要计算中毒的总时长&#xff0c;我们需要考虑时间点之间的差值&#xff0c;并根据这些差值来确定中毒的实际持续时间。 情况一&#xff1a;差值大于等于中毒时间 假设你的角色在时间点A中毒&#…