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

引言

在阅读开源项目源代码是,发现了一个有趣且特殊的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() 
C++中实现属性 本文译自http://www.codeguru.com/cpp_mfc/Property.html的Implementing a Property in C++ 以下是译文 本文由Emad Barsoum投稿。 开发测试环境:Visual C++ 7.0, Windows XP sp1, Windows 2000 sp3 摘要 本文试着在C++中不使用任何扩展技术模拟C#(或其他语言)中的属性特征。大多数在C++实现属性的库和编译器使用扩展技术,如Managed C++C++ Builder,或者他们使用如通常函数的set和get方法,但那不是属性。 详述 我们首先看一下什么是属性。一个属性表现为一个字段或者成员变量,但它通过read和write方法或者get和set方法暗中操作变量。 例如,若存在类A和它的属性Count,我可以写如下的代码: A foo; Cout << foo.Count; 实际上Count调用它的get函数返回当前的变量值。你可以将属性定为只读(你可以读取它但不能修改它)、只写或者可读写,这就是使用属性而不直接使用变量的的一个最大好处了。好了,让我们开始来实现它: 我们需要能做如下的事: int i = foo.Count; //--调用get函数得到值 foo.Count = i; //-- 调用set函数设定值 因此,很明显的我们需要重载 = 操作符使其能设定变量的值,同时也要重载该属性的返回值(在下面我们将会看到的)。 我们将实现一个称为property的类,它做的就像一个属性,声明如下: template class property {} 这个模板类表示的是我们的属性。Container是我们要在其中包含属性的类变量,set和get方法以及属性的类的类型。ValueType是内部变量即要定义的属性的类型,nPropType定义属性的读写标志:只读、只写或可读写。 现在我们需要一个指向从包含属性的类Container到属性类property的set和get方法的指针,同时重载 = 操作符以使得属性能象变量起那样作用。现在我们来看property类的全部定义 #define READ_ONLY 1 #define WRITE_ONLY 2 #define READ_WRITE 3 template class property { public: property() { m_cObject = NULL; Set = NULL; Get = NULL; } //-- 将m_cObject指向包含属性的container类 -- void setContainer(Container* cObject) { m_cObject = cObject; } //-- 设定可改变属性值的set成员函数 -- void setter(void (Container::*pSet)(ValueType value)) { if((nPropType == WRITE_ONLY) || (nPropType == READ_WRITE)) Set = pSet; else Set = NULL; } //-- 设定可检索属性值的get成员函数 -- void getter(ValueType (Container::*pGet)()) { if((nPropType == READ_ONLY) || (nPropType == READ_WRITE)) Get = pGet; else Get = NULL; } //-- 重载 = 号操作符使其能用set成员设定属性值-- ValueType operator =(const ValueType& value) { assert(m_cObject != NULL); assert(Set != NULL); (m_cObject->*Set)(value); return value; } //-- 使属性类能转换为内部类型成为可能-- operator ValueType() { assert(m_cObject != NULL); assert(Get != NULL);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值