[[maybe_unused]] 在成员变量上,GCC 警告(错误地?)属性被忽略

[[maybe_unused]] on member variable, GCC warns (incorrectly?) that attribute is ignored

在下面example:

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;

据我所知,这应该是合法的(并且不会被编译器忽略)。根据 standard:

10.6.7 Maybe unused attribute [dcl.attr.unused]
...
2. The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data member, a function, an enumeration, or an enumerator.
...

我讨厌挥动 "compiler bug" 锤子,但我不确定在这种情况下它还能是什么。

有没有人有任何见解?

任何属性都可以出于任何原因 "ignored by the compiler",除非标准另有规定(例如在明确禁止的位置使用属性)。

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

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 时会发出警告。

As far as I can tell, this should be legal (and not ignored by the compiler).

更正:

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

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

The attribute may be applied to the declaration of a class, a typedef-name, a variable (including a structured binding declaration), a non-static data member, a function, an enumeration, or an enumerator.

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

Recommended practice: For an entity marked maybe_­unused, implementations should not emit a warning that the entity or its structured bindings (if any) are used or unused. For a structured binding declaration not marked maybe_­unused, implementations should not emit such a warning unless all of its structured bindings are unused.

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

// 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 都没有针对上述情况发出警告,并且没有与本地 class 或 [=97= 的“未使用 public 字段相关的警告] 隐藏内部链接”既不是 GCC 也不是 Clang。

那么我们可能会问自己为什么 Clang 不会 发出一个实现定义的警告,即对于非静态数据成员,该属性将被忽略?原因是 Clang 确实为未使用的 private 静态数据成员发出 -Wunused-private-field warning

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 实现的功能:

已经

... Confirming as an enhancement.

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

Clang suppresses the warning if the member declaration has attribute unused, which we would need to do too.


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

由于这里没有编译器错误需要解决,Foo class(如果它有,比如说,(可能)未使用的私有数据成员),w.r.t。未使用的警告,例如是使用特定于实现的编译指示,如所示在 中,尝试调整所选编译器的实现定义行为。

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

请注意,以下内容即使表面上正确,实际上也行不通。只有更广泛的选项 -Wno-attributes 才能抑制此警告。


一种解决方法是为 gcc 使用标志 -Wno-ignored-attributes

-Wno-ignored-attributes (C and C++ only)

This option controls warnings when an attribute is ignored. This is different from the -Wattributes option in that it warns whenever the compiler decides to drop an attribute, not that the attribute is either unknown, used in a wrong place, etc. This warning is enabled by default.


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

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

Detecting compiler in CMake
Adding compiler flags in CMake