[[maybe_unused]] 在成员变量上,GCC 警告(不正确?)该属性被忽略



在下面的示例中:

struct Foo {
[[maybe_unused]] int member = 1;
void bar() {
[[maybe_unused]] int local = 0;
}
};
int main(int argc, char* argv[]) {
Foo f{};
f.bar();
return 0;
}

GCC 发出警告,而 Clang 和 MSVC 不会:

warning: 'maybe_unused' attribute ignored [-Wattributes]
[[maybe_unused]] int member = 1;

据我所知,这应该是合法的(而不是被编译器忽略)。 根据标准:

10.6.7 也许未使用的属性 [dcl.attr.unused]
...
2. 该属性可以应用于类、typedef-name、变量、非静态数据成员、函数、枚举或枚举器的声明。

我讨厌在"编译器错误"锤子周围摇摆,但我不确定在这种情况下还能是什么。

有人有见识吗?

任何属性都可以出于任何原因"被编译器忽略",除非标准另有规定(例如在明确禁止的位置使用属性)。

GCC并不是说你不能把一个放在那里;而是说把一个放在那里不会做任何事情,因为他们可能不会警告可能未使用的成员变量。

据我所知,这应该是合法的(而不是被编译器忽略)。

校正:

  • 是合法的:是的,它可以应用于非静态数据成员的声明,
  • 不应忽略:不,由实现决定是否以及如何使用此属性。

虽然 [dcl.attr.unused]/2 指定maybe_unused属性可以应用于非静态数据成员的声明 [强调我的]:

该属性可以应用于类、typedef-name、变量(包括结构化绑定声明)、非静态数据成员、函数、枚举或枚举器的声明。

对于如何应用此属性的实现没有严格的要求,只有关于实现应该如何应用它的建议,如 [dcl.attr.unused]/4 [强调我的]:

建议的做法:对于标记为 maybe_unused 的实体,实现不应发出警告,指出该实体或其结构化绑定(如果有)已使用或未使用。对于未标记为maybe_­unused的结构化绑定声明,除非其所有结构化绑定都未使用,否则实现不应发出此类警告。

这意味着只要实现允许将其应用于非静态数据成员的声明,它们就是符合标准的,并且该属性不使用推荐的做法实现并不是编译器的错误,即使我们可以争辩说编译器应该能够诊断在单个翻译单元中通过内部链接定义的类的未使用非静态数据成员。 例如,在以下示例中:

// test.cpp
namespace {
struct Foo {
int member{1};
void bar() {
[[maybe_unused]] int local = 0;
}
};
void bar() {
Foo f{};
f.bar();
}
}  // namespace

不使用Foo的非静态数据成员member;这是可诊断的,maybe_unused属性可以说可用于抑制此类实现定义的未使用警告。然而,GCC和Clang都没有对上述情况发出警告,并且对于GCC和Clang都没有与"本地类或隐藏有内部链接的类的未使用公共领域"相关的警告。

那么我们自己可能就像为什么 Clang发出实现定义的警告,即对于非静态数据成员的情况,该属性将被忽略?原因是 Clang 确实对未使用的私有静态数据成员发出-Wunused-private-field警告:

struct Foo {
void bar() {
int local = 0;
}
private:
int member{1};
// Clang: warning: private field 'member' is not used 
};

而GCC没有,这也包括为什么GCC(正确)警告非静态数据成员(甚至是私有成员)的maybe_unused属性将被忽略,因为它根本不会诊断未使用的私有数据成员(而Clang会)。这些行为都是正确的,因为它属于实施定义的行为领域。

我们可能会注意到,2016 年存在一个 GCC 错误报告,要求提供 Clang 实现的功能:

  • 错误 72789 - 添加 -Wunused-private-field

这已经

。确认为增强功能。

在重复标记的错误报告中,错误 87409 - 实现 -Wunused-private-field,JonathanWakely 评论说,如果要在 GCC 中实现此功能,他们还需要为(可能)未使用的属性实现抑制:

如果成员声明具有未使用的属性,Clang 会禁止显示警告,我们也需要这样做。


处理实现定义行为中的实现差异

由于这里没有编译器错误需要敲定,因此Foo类的"可移植"(对于选择的特定编译器)实现(如果它有,比如说,(也许)未使用的私有数据成员),w.r.t.未使用的警告,例如使用特定于实现的编译指示,如@0x5453所示:自回答,以尝试对齐所选编译器的实现定义行为。

另一种方法是考虑完全删除Clang的-Wunused-private-field警告全局(-Wno-unused-private-field),将这些类型的诊断留给静态分析工具。

GCC 首先不会警告您未使用的成员变量,因此该属性没有目的,这就是为什么它会警告您忽略它的原因。这只是一个警告,你的代码仍然是合法的。

现在我再次查看此内容,我无法让 Clang 警告未使用的成员,因此您可以删除该属性以满足所有编译器的要求。

如果要在 GCC 中完全禁用警告,可以使用以下编译标志:

-Wno-attributes

下面是一个有选择地禁用警告的示例,但它并不漂亮:

struct Foo {
#ifdef __GNUC__
#  pragma GCC diagnostic push
#  pragma GCC diagnostic ignored "-Wattributes"
#endif
[[maybe_unused]] int member = 1;
#ifdef __GNUC__
#  pragma GCC diagnostic pop
#endif
void bar() {
[[maybe_unused]] int local = 0;
}
};
int main(int argc, char* argv[]) {
Foo f{};
f.bar();
return 0;
}

#ifdef __GNUC__是必需的,因为 MSVC 在看到#pragma GCC时会发出警告。

请注意,以下内容即使看起来正确,实际上也不起作用。只有更广泛的选项-Wno-attributes抑制此警告。


一种解决方法是将标志-Wno-ignored-attributes

-Wno-ignored-attributes(仅限 C 和 C++)

此选项控制忽略属性时的警告。这与-Wattributes选项不同,因为它会在编译器决定删除属性时发出警告,而不是该属性未知、在错误的位置使用等。默认情况下,此警告处于启用状态。


如果您使用的是 CMake,这将如下所示:

if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# using GCC
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ignored-attributes")
endif ()

在 CMake
中检测编译器在 CMake 中添加编译器标志

最新更新