C++属性的条件编译适配:旧版编译器兼容实践问询
旧版C++兼容下的[[nodiscard]]/[[noreturn]]/[[fallthrough]]属性处理
是否需要针对每个属性做条件编译?
必须做。这三个属性的原生支持版本在g++、clang、VS里差异不小:
- [[noreturn]]是C11标准引入的,g4.8+、clang3.3+、VS2015(
_MSC_VER=1900)起原生支持 - [[nodiscard]]和[[fallthrough]]是C17标准引入的,g7+、clang5+、VS2017(
_MSC_VER=1910)起才支持
如果直接在不支持的编译器版本里写这些属性,会触发编译错误,不是简单忽略,所以必须像你给出的参考代码那样,通过条件编译来封装这些属性。
C++11起三大编译器对未知[[attribute]]的处理
C14标准规定未知属性是实现定义行为,C17要求编译器必须忽略未识别属性,但三大编译器的实际表现有差异:
- g++:在C11模式下(
-std=c++11),遇到未知属性会弹出警告(默认不终止编译),可以加-Wno-attributes参数关闭警告;切换到C17模式后会完全忽略,无任何提示。 - clang:从C11模式开始,遇到未知属性直接忽略,默认不会发警告;C17模式下和标准要求一致。
- VS:VS2015及以后版本(支持C++11及以上),遇到未知属性会自动忽略,只有开启
/W4及以上警告等级时才会弹出提醒,不会导致编译失败。
C++11前编译器的实际表现
C++11标准才引入[[...]]属性语法,所以更早版本的编译器完全不识别这个语法:
- g++4.7及更早:直接把
[[...]]当成语法错误,编译失败。 - clang3.2及更早:同样识别不了
[[...]]语法,编译报错。 - VS2013及更早:完全不支持属性语法,代码里写
[[...]]会直接触发编译错误。
优化后的兼容宏示例
除了检查__cplusplus版本,还可以结合编译器版本宏做更精准的兼容,避免因为编译器部分支持标准但未更新__cplusplus宏导致的问题:
// [[noreturn]] 兼容定义 #if (__cplusplus >= 201103L) || \ (__GNUC__ >= 4 && __GNUC_MINOR__ >= 8) || \ (__clang_major__ >= 3 && __clang_minor__ >= 3) || \ (_MSC_VER >= 1900) #define MyNamespace_NoReturn [[noreturn]] #else #define MyNamespace_NoReturn #endif // [[nodiscard]] 兼容定义 #if (__cplusplus >= 201703L) || \ (__GNUC__ >= 7) || \ (__clang_major__ >= 5) || \ (_MSC_VER >= 1910) #define MyNamespace_NoDiscard [[nodiscard]] #else #define MyNamespace_NoDiscard #endif // [[fallthrough]] 兼容定义 #if (__cplusplus >= 201703L) || \ (__GNUC__ >= 7) || \ (__clang_major__ >= 5) || \ (_MSC_VER >= 1910) #define MyNamespace_Fallthrough [[fallthrough]] #else #define MyNamespace_Fallthrough #endif
内容的提问来源于stack exchange,提问作者Swiss Frank
相关产品推荐
相关产品推荐

