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

时间:2018-06-01 15:04:57

标签: c++ c++17 compiler-warnings compiler-bug

在以下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;

据我所知,这应该是合法的(编译器不会忽略)。根据{{​​3}}:

  

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

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

有没有人有任何见解?

5 个答案:

答案 0 :(得分:4)

<块引用>

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

更正:

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

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

<块引用>

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

对于如何应用这个属性没有严格的要求,只有一个建议关于实现应该如何应用它,如根据{{​​3}} [强调我的]:

<块引用>

推荐做法:对于标记为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

未使用member的非静态数据成员Foo;这是可诊断的,可以使用 maybe_unused 属性来抑制这种实现定义的未使用警告。但是,GCC 和 Clang 都没有针对上述情况发出警告,而且事实上,GCC 和 Clang 都没有与“本地类的未使用 public 字段或使用内部链接隐藏的类”相关的警告。

那么我们可能会想为什么 Clang 不发出实现定义的警告,即对于非静态数据成员的情况,该属性将被忽略?原因是 Clang 确实为未使用的 private 静态数据成员发出了 [dcl.attr.unused]/4

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

曾经

<块引用>

...确认为增强功能。

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

<块引用>

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


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

由于这里没有编译器错误需要解决,因此 Foo 类的“可移植”(对于所选的特定编译器)实现(如果它具有,比如说,(也许)非使用私有数据成员),wrt未使用的警告,例如是使用特定于实现的编译指示,如图所示,例如在 Bug 87409 - Implement -Wunused-private-field 中,尝试对齐所选编译器的实现定义行为。

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

答案 1 :(得分:3)

任何属性都可以被编译器忽略&#34;出于任何原因,除非标准另有说明(例如在明确禁止的地方使用属性)。

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

答案 2 :(得分:2)

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

答案 3 :(得分:1)

现在我再次查看此内容,我无法让 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 时会发出警告。

答案 4 :(得分:0)

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


一种解决方法是为 gcc 使用标志 -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 ()

Detecting compiler in CMake
Adding compiler flags in CMake

相关问题