c++20中的属性

本文详细介绍了C++中的属性特性,从C++11到C++20的发展,强调了属性如何增强代码的清晰性和效率。C++20引入了如`[[nodiscard]]`、`[[fallthrough]]`等标准属性,并提供了`[[attribute-list]]`语法。通过实例展示了`nodiscard`和`fallthrough`的用法,表明属性能有效提高代码可读性,减少错误。

一、属性

其实属性这个词出现很早,属性这个词在不同的环境下有不同的语义。在英文中体现为一个是“Property”一个是“Attribute”,在本文中,是对后者的说明。属性,其实可以认为是一种对象的基础特征。有过Linux下编程的都知道,在GUN C中,有编程语言扩展 attribute((…))关键字,而在Windows平台上有编程语言扩展 __declspec() 等语法。统一的属性关键字在Java、c#等语言中早就普及使用,但在c++标准中,仍然没有支持,一直到c++11才有了改观。

二、c++20中的属性

从c++11始到c++20,规则不断完善,提供了以双中括号显示指定的语法标志,如下:

Introduces implementation-defined attributes for types, objects, code, etc.

[[attr]] [[attr1, attr2, attr3(args)]] [[namespace::attr(args)]] alignas_specifier
Formally, the syntax is

[[ attribute-list ]]		(since C++11)
[[ using attribute-namespace : attribute-list ]]		(since C++17)
where attribute-list is a comma-separated sequence of zero or more attributes (possibly ending with an ellipsis ... indicating a pack expansion)

identifier	(1)	
attribute-namespace :: identifier	(2)	
identifier ( argument-list )	(3)	
attribute-namespace :: identifier ( argument-list )	(4)	
1) simple attribute, such as [[noreturn]]
2) attribute with a namespace, such as [[gnu::unused]]
3) attribute with arguments, such as [[deprecated("because")]]
4) attribute with both a namespace and an argument list
If using namespace: appears in the beginning of an attribute list, no other attributes in the attribute list can specify a namespace: the namespace specified in a using applies to them all:

[[using CC: opt(1), debug]] // same as [[CC::opt(1), CC::debug]]
[[using CC: CC::opt(1)]] // error: cannot combine using and scoped attribute(since C++17)

在c++的标准中定义了如下的属性:

C++ 标准仅定义下列属性。

[[noreturn]](C++11)	指示函数不返回
[[carries_dependency]](C++11)	指示释放消费 std::memory_order 中的依赖链传入和传出该函数。
[[deprecated]](C++14)
[[deprecated("原因")]](C++14)	指示允许使用声明有此属性的名称或实体,但因 原因 而不鼓励使用。
[[fallthrough]](C++17)	指示从前一 case 标号直落是有意的,而在发生直落时给出警告的编译器不应该为此诊断。
[[nodiscard]](C++17)
[[nodiscard("原因")]](C++20)	鼓励编译器在返回值被舍弃时发布警告。
[[maybe_unused]](C++17)	压制编译器在未使用实体上的警告,若存在。
[[likely]](C++20)
[[unlikely]](C++20)	                指示编译器应该针对通过某语句的执行路径比任何其他执行路径更可能或更不可能的情况进行优化。
[[no_unique_address]](C++20)	指示非静态数据成员不需要拥有不同于其类的所有其他非静态数据成员的地址。
[[optimize_for_synchronized]](TM TS)	指示应该针对来自 synchronized 语句的调用来优化该函数定义

在GCC和Clang中定义的相关属性可参看如下资料:
https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html#Attribute-Syntax
https://clang.llvm.org/docs/AttributeReference.html

其实使用属性的优点还是非常明显的,一些常用的变量或者语法,可以直接指定而不需要在程序内不断的重复相关的动作和代码,显得代码更清晰整洁,易于理解。

三、例程

在前面分支预测其实就是一个属性的例子,这里再看几个:

//nodiscard

struct [[nodiscard]] error_info { };
error_info enable_missile_safety_mode();
void launch_missiles();
void test_missiles() {
   enable_missile_safety_mode(); // 编译器可在舍弃 nodiscard 值时发布警告
   launch_missiles();
}
error_info& foo();
void f1() {
    foo(); // 并非按值返回 nodiscard 类型,无警告
}

//fallthrough

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

用上属性,是不是更清晰,不用再写注释让人再去分辨对错了。简单就是美,简单就是王道。
更多细节,请参阅:
https://en.cppreference.com/w/cpp/language/attributes

四、总结

其实越看c++的标准,越看C++新的标准,其实和其它语言当初那锋利的界限越来越模糊,这也是一个不争的事实,也是一种天然的进步。每个语言在迭代过程中,所经历的问题的大抵是相似的,而解决问题的方法和手段在计算机领域内也就那几种,所以殊途同归,最终的表现形式,一定是你中有我,我中有你,不可能再象以前,边边角角的都是那样的棱角分明。
有借鉴,才有创新;有吸收,才会有创新;更合适,就是创新。
在这里插入图片描述

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、付费专栏及课程。

余额充值