C++属性语法

本文详细介绍了C++的属性语法,比较了MSVC和GCC的实现,列举了9种C++标准属性及其用途,包括noreturn、deprecated、fallthrough等,以及如何在实际编程中使用这些特性以提高代码可读性和移植性。
摘要由CSDN通过智能技术生成

问题来源

用Visual Studio 2019查看VTK源码时看到

// QVTKWidget.h
// QVTKWidget的有两个参数的构造函数被VTK_LEGACY宏包围
VTK_LEGACY(QVTKWidget(QWidget *parent = nullptr, Qt::WindowFlags f = 0));

在vtkSetGet.h中找到了该宏的定义

# if defined(__GNUC__) && !defined(__INTEL_COMPILER)
#  define VTK_LEGACY(method) method __attribute__((deprecated))
# elif defined(_MSC_VER)
#  define VTK_LEGACY(method) __declspec(deprecated) method
# else
#  define VTK_LEGACY(method) method
# endif

其中__attribute__() 为GCC属性语法,__declspec()为MSVC语法
对于__declspec()进行查阅

C++属性语法

标准C++

不同编译器属性语法不相同,会降低代码的可移植性,因此自C++11开始,标准C++也引入了属性语法
[[attr]]
[[attr1, attr2, attr3(args)]]
[[namespace::attr(args)]]

MSVC

__declspec(attr1, [attr2], […])

GCC

__attribute__(attr1, [attr2], […])

9种C++标准属性

简表

attribute-token属性标准作用
carries_dependency[[carries_dependency]]200809L(C++11)指示释放消费 std::memory_order 中的依赖链传进和传出该函数,这允许编译器跳过不必要的内存栅栏指令。
noreturn[[noreturn]]200809L(C++11)表示函数不返回值,并只能应用在函数上面,如果函数真的有返回值,那么该属性无效,并且会给出警告
deprecated[[deprecated]][[deprecated(“reason”)]]201309L(C++14)表示某些实体已经废弃,或者因为某些原因废弃,可以用在类,定义类型名,变量,非静态数据成员,枚举,模板实例
fallthrough[[fallthrough]]201603L(C++17)指示从前一 case 标号直落是有意的,而在发生直落时给出警告的编译器不应该为此诊断。
maybe_unused[[maybe_unused]]201603L(C++17)压制编译器在未使用实体上的警告,若存在。
nodiscard[[nodiscard]][[nodiscard(“reason”)]]201603L(C++17)(C++20)若返回值被舍弃,则鼓励编译器发布警告。
likely[[likely]]201803L(C++20)指示编译器应该针对通过某语句的执行路径比任何其他执行路径更可能或更不可能的情况进行优化。
unlikely[[unlikely]]201803L(C++20)指示编译器应该针对通过某语句的执行路径比任何其他执行路径更可能或更不可能的情况进行优化。
no_unique_address[[no_unique_address]]201803L(C++20)指示非静态数据成员不需要拥有不同于其类的所有其他非静态数据成员的地址。

1. noreturn(C++11)

noreturn用于声明函数不会返回
与void不同的是,void实际上是返回给调用者,noreturn自己处理程序剩余的逻辑,不会返回到函数调用点
这种写法一般适用于处理一些致命错误或者是异常

C++的abort、exit、terminate等函数都具有[[noreturn]]属性
当使用[[noreturn]]属性标识函数后,调用该函数的后面的语句会得到编译器提示,warning C4702: 无法访问的代码。这将有助于发现遗漏未决问题。

#include <iostream>
using namespace std;
[[noreturn]] void foo()
{
    cout << "foo called" << endl;
    exit(0); // 这里直接退出程序,不需要返回主函数
}

void bar()
{
    cout << "bar called" << endl;
}

int main(int argc, char *argv[])
{
    foo();
    bar();
}

2. carries_dependency

[[carries_dependency]] 属性指定函数传播线程同步的数据依赖项顺序。 可将该属性应用于一个或多个参数,以指定传入的参数要将依赖项带入函数主体中。 可将该属性应用于函数本身,以指定返回值要将依赖项带出函数。 编译器可以使用此信息来生成更有效的代码。

3. deperacated

弃用指示,即属性的名字或者实体被弃用,允许但是不鼓励使用

语法
[[deprecated]]
[[deprecated( "A string" )]]
// e.g.
#include <iostream>
using namespace std;
// 弃用类 结构体 联合 枚举
class [[deprecated("this is deprated")]] C{};
struct [[deprecated]] S{};
union  [[deprecated]] U{};
enum [[deprecated]] E{A};

// 弃用枚举中的某几项
enum E1{B,G[[deprecated]]};


// 弃用别名
[[deprecated]] typedef wchar_t* WSTR;
using STR [[deprecated]] = char*;

// 弃用变量
[[deprecated]] int a;
class C1{
public:
    [[deprecated]] int b;
    [[deprecated]] static int c;
};

// 弃用函数
[[deprecated]] void func()
{

}


int main() {
    // 弃用类名等
    C c;
    S s;
    U u;
    E::A;

    // 弃用别名
    WSTR wstr;
    STR str;

    // 弃用变量
    a = 1;
    C1 c1;
    c1.b;
    c1.c;
    // 弃用函数
    func();
    E1::G;
    E1::B;
    std::cout << "mian end" << std::endl;
}

4. fallthrough

[[fallthrough]] 属性可在 switch 语句的上下文中用作向编译器(或阅读代码的任何人)显示的提示,指出失败行为是有意的。 Microsoft C++ 编译器当前不会对回退行为发出警告,因此此属性对编译器行为没有影响。

5. nodiscard

Visual Studio 2017 版本 15.3 和更高版本:(适用于 /std:c++17 和更高版本。)指定不应该丢弃函数的返回值。 引发警告 C4834,如以下示例中所示:

[[nodiscard]]
int foo(int i) { return i * i; }

int main()
{
    foo(42); //warning C4834: discarding return value of function with 'nodiscard' attribute
    return 0;
}

6. maybe_unused

Visual Studio 2017 版本 15.3 及更高版本:(适用于 /std:c++17 及更高版本。)[[maybe_unused]] 属性指定变量、函数、类、typedef、非静态数据成员、枚举或模板专用化可能是有意不使用的。 当未使用标记为 [[maybe_unused]] 的实体时,编译器不会发出警告。 未使用属性声明的实体以后可以使用属性来重新声明,反之亦然。 分析某个实体的首个标记为 [[maybe_unused]] 的声明后,该实体被视为已标记,适用于当前转换单元的其余部分。

7. likely

Visual Studio 2019 版本 16.6 及更高版本:(适用于 /std:c++20 及更高版本。)[[likely]] 属性向编译器指定提示,指出属性化标签或语句的代码路径的执行可能性高于替代项。 在 Microsoft 编译器中,[[likely]] 属性将块标记为“热代码”,这会增加内部优化分数。 针对速度进行优化时,分数增加得更多;针对大小进行优化时,增加的幅度不太大。 净分数会影响内联、循环展开和向量化优化的可能性。 [[likely]] 和 [[unlikely]] 的效果类似于按配置优化,但仅限于当前转换单元的范围。 该属性尚未针对此属性实施块重新排序优化。

8. unlikely

Visual Studio 2019 版本 16.6 及更高版本:(适用于 /std:c++20 及更高版本。)[[unlikely]] 属性向编译器指定提示,指出属性化标签或语句的代码路径的执行可能性低于替代项。 在 Microsoft 编译器中,[[unlikely]] 属性将块标记为“冷代码”,这会减少内部优化分数。 针对大小进行优化时,分数减少得更多;针对速度进行优化时,减少的幅度不太大。 净分数会影响内联、循环展开和向量化优化的可能性。 该属性尚未针对此属性实施块重新排序优化。

9. no_unique_address

如果空成员子对象使用属性 [[no_unique_address]],那么允许像空基类一样优化掉它们。取这种成员的地址会产生可能等于同一个对象的某个其他成员的地址。
//为保证同一类型的不同对象地址始终有别,要求任何对象或成员子对象的大小至少为 1,即使该类型是空的类类型

struct Base {}; // 空类,占用1字节
 
//如果空成员子对象使用属性 [[no_unique_address]],那么允许像空基类一样优化掉它们。
struct Base1 {
    [[no_unique_address]] Base c1;  //主动优化,空成员c1被优化掉
    Base c2;  // Base,占用 1 字节,后随对 i 的填充      
    int i;    // int成员占用 sizeof(int) 字节      
};
//
    // 应用空基类优化
    std::cout <<"sizeof(Base1)  = "<< sizeof(Base1) <<"\n";      //8
struct Empty {}; // 空类
 
struct X {
    int i;
    Empty e;    //Empty成员占用1字节,后随3个填充字节以满足 int 的对齐要求 
};
 
struct Y {
    int i;
    [[no_unique_address]] Empty e;//主动优化,空成员e被优化掉
};
 
struct Z {
    char c;
    //e1 与 e2 不能共享同一地址,因为它们拥有相同类型,其中一者可以与 c 共享地址
    [[no_unique_address]] Empty e1, e2;//主动优化,空成员e1、e2其中一个被优化掉
};
 
struct W {
    char c[2];
    //e1 与 e2 不能拥有同一地址,但它们之一能与 c[0] 共享,而另一者与 c[1] 共享
    [[no_unique_address]] Empty e1, e2;//主动优化,空成员e1、e2被优化掉
};
//
void no_unique_address_test(void)
{
    // 任何空类类型对象的大小至少为 1
    std::cout <<"sizeof(Empty)  = "<< sizeof(Empty) <<"\n";      //1
 
    // 至少需要多一个字节以给 e 唯一地址
    std::cout <<"sizeof(X)  = "<< sizeof(X) <<"\n"; //8
 
    // 优化掉空成员
    std::cout <<"sizeof(Y)  = "<< sizeof(Y) <<"\n";//4 
 
    // e1 与 e2 不能共享同一地址,因为它们拥有相同类型,尽管它们标记有 [[no_unique_address]]。
    // 然而,其中一者可以与 c 共享地址。
    std::cout <<"sizeof(Z)  = "<< sizeof(Z) <<"\n";//2
 
    // e1 与 e2 不能拥有同一地址,但它们之一能与 c[0] 共享,而另一者与 c[1] 共享
    std::cout <<"sizeof(W)  = "<< sizeof(W) <<"\n";//3
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值