首页 > 编程语言 >深入剖析C++的 “属性“(Attribute specifier sequence)

深入剖析C++的 “属性“(Attribute specifier sequence)

时间:2024-07-10 16:55:08浏览次数:17  
标签:specifier 函数 sequence int Attribute void C++ 编译器 属性

引言

在阅读开源项目源代码是,发现了一个有趣且特殊的C++特性:属性。

属性(attribute specifier sequences)是在C++11标准引入的。在C++11之前,编译器特有的扩展被广泛用来提供额外的代码信息。例如,GNU编译器(GCC)使用__attribute__来控制函数的行为。但是缺点也很明显,那就是这种方式缺乏标准化,不同编译器的特性并不兼容,严重影响了代码的可移植性。

为了解决这一问题,C++11标准引入了属性,提供一种标准化的方法来给编译器额外的信息。属性既可以应用于类型声明,也可以应用于声明语句、定义、代码块等,为编译器提供了关于编码意图的附加信息。

属性的作用

属性引入C++后,提供了以下几个方面的价值:

  1. 可移植性:提供了一种标准的方式来传达编译器特定的信息,减少了依赖特定编译器扩展的需要。

  2. 可读性:通过标准的属性,代码的特定行为和意图被清晰地表达,易于他人理解。

  3. 健壮性:属性可以帮助检测潜在的错误,如忽略了重要的函数返回值。

  4. 性能:许多属性可以帮助编译器进行优化,提高程序性能。

常见属性

以下是C++中一些常用的属性:

  1. [[noreturn]] - 表明函数不会返回到调用者。这通常用于那些通过抛出异常或终止程序来退出的函数。

  2. [[nodiscard]] - 用于函数或类型,标明调用者不应忽略返回值。对于类型,它表示该类型的对象或其构造函数返回的对象不应被忽略。

  3. [[deprecated]] - 标记某个实体(如函数、类、类型别名、变量等)为过时的,建议不要使用。可选地,可以提供一条消息说明替代的使用方式。

  4. [[fallthrough]] - 在C++17中引入,用于switch语句的case节中,明确表示允许控制流从当前case分支无条件跳转到下一个case分支的行为,避免编译器生成可能的警告。

  5. [[maybe_unused]] - 表示某个实体(如函数、类、变量等)可能不会被使用,从而防止编译器发出未使用警告。

  6. [[likely]][[unlikely]] - 这两个属性是在C++20中引入的,用来显式地指示给定的布尔表达式结果的可能性。[[likely]]表示表达式结果为true的可能性更高,而[[unlikely]]表示结果为false的可能性更高。

  7. [[no_unique_address]] - 在C++20中引入,它指示类成员不必拥有唯一的地址,允许空基优化或类似的优化技术。

这些属性可以用来改善代码的文档化、提高性能、帮助编译器检查错误和警告,以及启用或禁用特定的编译器行为。
它们是现代C++编程中代码质量和维护方面的重要工具。不同的编译器可能对属性的支持程度有所不同。

常用属性示例

[[noreturn]]

[[noreturn]]属性指示某个函数不会返回到调用者。这通常用于那些通过抛出异常或终止程序来退出的函数。

[[noreturn]] void terminate_program() {
    // ... 清理操作 ...
    std::exit(1); // std::exit不返回
}

[[nodiscard]]

[[nodiscard]]属性用于函数或类型,标明调用者不应忽略返回值。这有助于防止编码中的错误,例如忘记处理函数返回的错误代码。

[[nodiscard]] int compute() {
    // ... 计算操作 ...
    return result;
}

void example() {
    compute(); // 如果忽略了结果,编译器可能会警告
}

C++20对nodiscard进行了扩展,支持注明原因。格式为:[[nodiscard(“reason”)]]

[[deprecated]]

[[deprecated]]属性用于标记过时的实体。

[[deprecated("Use new_function instead")]]
void old_function() {
    // ...
}

void example() {
    old_function(); // 使用这个函数时,编译器会给出警告
}

[[fallthrough]]

[[fallthrough]]属性用在switch语句中,表示有意识的case穿透。

void example(int val) {
    switch (val) {
        case 1:
            // 计算某些事情
            [[fallthrough]]; // 明确表明穿透是有意为之
        case 2:
            // 1和2执行相同的代码
            break;
        default:
            // 其他值处理
            break;
    }
}

[[maybe_unused]]

[[maybe_unused]]属性用于可能不使用的变量或函数,防止编译器发出未使用警告。

[[maybe_unused]] static bool is_debug = true;

void example() {
    [[maybe_unused]] int local_variable = compute();
}

[[likely]] 和 [[unlikely]]

[[likely]][[unlikely]]在C++20中引入,帮助编译器优化分支预测。

bool condition = /* ... */;
if ([[likely]] condition) {
    // 大多数情况下,条件为真时的代码
} else {
    // 条件为假时的代码
}

其他不常用的属性介绍

[[carries_dependency]] (C++11)

[[carries_dependency]]属性用来指示在函数参数或返回值中的依赖关系链可以传递,这在使用std::memory_order时或在多线程编程中非常重要。

#include <atomic>
#include <iostream>

std::atomic<int> global_data;

// 该函数表明参数和返回值带有依赖关系链
[[carries_dependency]] int load_data(std::atomic<int>& data) {
    return data.load(std::memory_order_consume);
}

void process_data() {
    // 从全局数据载入的依赖关系被保留
    int local_data = load_data(global_data);
    // 接下来的操作可以依赖于这个顺序
}

[[no_unique_address]] (C++20)

[[no_unique_address]]属性表示非静态数据成员不必具有唯一的地址,这允许编译器在可能的情况下进行内存优化。在C++中,即使是完全空的类(不含任何成员变量或成员函数)也至少会占用1字节的大小,这是为了确保每个对象都有一个唯一的地址。但是,有时候这个额外的1字节并不是必须的,例如当空类作为其他类的成员时,这在包含多个空类成员的大型结构体中可以节省大量内存。

struct Empty {}; // empty class
 
struct X
{
    int i;
    Empty e;
};
 
struct Y
{
    int i;
    [[no_unique_address]] Empty e;
};
  
int main()
{
    // the size of any object of empty class type is at least 1
    static_assert(sizeof(Empty) >= 1); 
    // at least one more byte is needed to give e a unique address
    static_assert(sizeof(X) >= sizeof(int) + 1); 
    static_assert(sizeof(Y) == sizeof(int)); 
}

[[assume(expression)]] (C++23)

[[assume(expression)]]属性告诉编译器,在给定的点上,表达式总是评估为真。这有助于编译器进行优化。

void process_data(int* ptr) {
    // 告诉编译器ptr不为nullptr,这有助于优化
    [[assume(ptr != nullptr)]]
    *ptr = 42;
}

[[indeterminate]] (C++26)

[[indeterminate]]属性是一个假设的属性,用来指明一个对象如果没有被初始化,则具有不确定的值。由于C++26还目前还未发布,暂时用不了。

void f(int); 
void g()
{
    int x [[indeterminate]]; // indeterminate value
    int y;                   // erroneous value
 
    f(x); // 允许编译通过,但是具有不确定的行为
    f(y); // 编译报错,不允许使用未初始化的值
}

[[optimize_for_synchronized]] (TM TS)

[[optimize_for_synchronized]]属性来自事务性内存技术规范(TM TS),它建议函数应该为同步块中的调用进行优化。TM TS不是ISO C++标准的一部分,支持度因编译器而异。

// 这个属性建议函数在同步块中调用时应该进行优化
[[optimize_for_synchronized]] void synchronized_function() {
    // 在同步语句中频繁调用的函数
}

结语

笔者最喜欢的C++属性就是[[nodiscard]]了,计划今天就在团队中推广开。因为许多开发者在调用一些可能失败的函数不检查返回值,导致代码鲁棒性较低。给一些重要函数加上[[nodiscard]]属性之后,编译器就能避免这种情况的发生,真是太有用了。试了一下,MSVC下nodiscard会出现警告,还好我们开了警告视为错误,那么就可以确保开发者不会忘记处理返回值了。
在这里插入图片描述

如果向了解更多关于C++属性的知识,那么可以来cppreference看看。cppreference的C++的属性参考:Attribute specifier sequence(since C++11)

标签:specifier,函数,sequence,int,Attribute,void,C++,编译器,属性
From: https://blog.csdn.net/hebhljdx/article/details/140314672

相关文章

  • Failed to configure a DataSource: 'url' attribute is not specified and no embe..
    原文链接: https://www.cnblogs.com/javawxid/p/10949511.html问题原因:Mybatis没有找到合适的加载类,其实是大部分spring-datasource-url没有加载成功,分析原因如下所示.DataSourceAutoConfiguration会自动加载.没有配置spring-datasource-url 属性.spring......
  • 处理Keras中的AttributeError: ‘NoneType‘ object has no attribute ‘XYZ‘
    处理Keras中的AttributeError:'NoneType'objecthasnoattribute'XYZ'......
  • POJ3017 Cut the Sequence
    POJ3017CuttheSequence题目大意给定一个一个长度为\(N\)的序列\(A\),要求把该序列划分成若干段,其中每一段中的数的和不大于\(M\),现在需要使得每一段中数的最大值的和最小,求该最小值。\[0\leqn\leq10^5\\0\leqm\leq10^{11}\\0\leqa_i\leq10^6\]解题思路......
  • Leetcode 1143. Longest Common Subsequence
    ProblemGiventwostringstext1andtext2,returnthelengthoftheirlongestcommonsubsequence.Ifthereisnocommonsubsequence,return0.Asubsequenceofastringisanewstringgeneratedfromtheoriginalstringwithsomecharacters(canbenone......
  • Atcoder ABC091D Two Sequences
    首先根据\(\operatorname{xor}\),就想到拆成二进制的\(a_{i,w},b_{i,w}\)来处理。类似竖式加法,考虑把得到的结果\(c_{w}\)分为\(a_{i,w}+b_{j,w}+x\),其中\(x\)就是上一位的进位。进一步的,发现对于总的\(c_{w}\),\(a_{i,w},b_{j,w}\)肯定都在这个位置加了\(......
  • Unity 编辑拓展使用Attribute 实现面板按钮
    unity面板按钮工具(1)完成效果原效果代码部分namespaceColorzoreTools{usingSystem;usingUnityEngine;publicclassTestAttribute:MonoBehaviour{[Button("测试")]publicvoidTestBtn(){//这个方法会被......
  • D. Invertible Bracket Sequences
    原题链接题解把(当作+1,)当作-1,我们可以得到这样的图像易得要保证翻完之后整体的合法性,\([l,r]\)内的左右括号数量要相等,在图上看就是\(pre[l-1]==pre[r]\)相等一个合法括号,要保证所有的\(pre[i]\)不小于0,因此反过来之后,最小的\(pre[i]\)等于\(pre[r]-\max(pre[k]),k\in......
  • Ragas实践问题记录2 AttributeError: ‘TestsetGenerator‘ object has no attribute
    报错问题依然是在尝试官方文档“CompareLLMsusingRagasEvaluations”的“Createsynthetictestdata”步骤发生报错。官方文档以及文档中代码如下:Ragas:CompareLLMsusingRagasEvaluations官方文档中的代码:importosfromllama_indeximportdownload_loader,Simp......
  • 汽车电子-如何用Test Sequence做自动化测试
    汽车电子-如何用Test Sequence做自动化测试附赠自动驾驶最全的学习资料和量产经验:链接如何用来做自动化测试呢?创建一个测试序列点击模型右键Test Harness/Creat for左边选择Sequence通过Harness进行调试修改后可以保存到原来模型中双击点开表格,在里面填入输入......
  • square869120Contest #3 G Sum of Fibonacci Sequence
    洛谷传送门AtCoder传送门特判\(n=1\)。将\(n,m\)都减\(1\),答案即为\[[x^m]\frac{1}{(1-x-x^2)(1-x)^n}\]若能把这个分式拆成\(\frac{A(x)}{(1-x)^n}+\frac{B(x)}{1-x-x^2}\)的形式,其中\(\degA(x)\len-1,\degB(x)\le1\),那么答案就是好算的。......