关于[[nodiscard]]属性在GCC与Clang下编译差异的技术问询
问题:[[nodiscard]]属性的误用与跨编译器行为差异
我尝试使用[[nodiscard]]属性禁止忽略A::create()的返回值,编写的代码如下:
#include <iostream> #include <memory> class A { public: static [[nodiscard]] std::unique_ptr<A> create(); virtual int get_version() = 0; virtual ~A() = default; }; class B : public A { public: [[nodiscard]] int get_version() override { return 20; } }; std::unique_ptr<A> A::create() { return std::make_unique<B>(); } int main() { auto a = A::create(); [[maybe_unused]] int v = a->get_version(); }
测试环境
- GCC版本:8.5
- Clang版本:15.0.0
- 编译选项:
-O3 -std=c++17
各编译器输出
GCC输出
<source>:7:12: warning: attribute ignored [-Wattributes] static [[nodiscard]] std::unique_ptr<A> create(); ^ <source>:7:12: note: an attribute that appertains to a type-specifier is ignored ASM generation compiler returned: 0 <source>:7:12: warning: attribute ignored [-Wattributes] static [[nodiscard]] std::unique_ptr<A> create(); ^ <source>:7:12: note: an attribute that appertains to a type-specifier is ignored Execution build compiler returned: 0 Program returned: 0
Clang输出
<source>:7:14: error: 'nodiscard' attribute cannot be applied to types static [[nodiscard]] std::unique_ptr<A> create(); ^ 1 error generated. ASM generation compiler returned: 1 <source>:7:14: error: 'nodiscard' attribute cannot be applied to types static [[nodiscard]] std::unique_ptr<A> create(); ^ 1 error generated. Execution build compiler returned: 1
MSVC输出
该代码在MSVC v19.33下可正常编译,无任何错误或警告。
疑问
请问我是否误用了[[nodiscard]]属性?为何不同编译器会出现这样的行为差异?
回答
你确实误用了[[nodiscard]]属性
在C17标准里,[[nodiscard]]仅允许修饰函数、枚举类或类,用来标记它们的返回值(或实例)不应被忽略。你当前的写法把[[nodiscard]]放在返回类型std::unique_ptr<A>前面,编译器会判定你试图修饰类型——而C17并不支持用[[nodiscard]]修饰类型(这个特性要到C++20才引入)。
正确的写法应让[[nodiscard]]明确修饰函数,以下两种写法都符合C++17标准:
// 写法1:属性放在函数返回类型之后 static std::unique_ptr<A> [[nodiscard]] create(); // 写法2:属性放在函数修饰符(static)之后、返回类型之前 [[nodiscard]] static std::unique_ptr<A> create();
不同编译器行为差异的原因
标准合规性处理不同:
- Clang 15严格遵循C++17标准,直接报错指出
[[nodiscard]]不能应用于类型,拒绝编译。 - GCC 8.5同样识别到写法不符合标准,但仅给出警告提示属性被忽略,仍继续编译。
- MSVC v19.33做了语法扩展,将你代码中的
[[nodiscard]]解析为修饰函数而非类型,因此能正常通过编译。
- Clang 15严格遵循C++17标准,直接报错指出
属性解析逻辑差异:
你的代码中[[nodiscard]]处于static和返回类型之间,GCC和Clang按照标准语法将其绑定到了后续的类型;而MSVC的解析逻辑更宽松,将其绑定到了函数,这就导致了跨编译器的行为不一致。
内容的提问来源于stack exchange,提问作者kiner_shah
相关产品推荐
相关产品推荐

