【问题标题】:[[maybe_unused]] on member variable, GCC warns (incorrectly?) that attribute is ignored[[maybe_unused]] 在成员变量上,GCC 警告(不正确?)该属性被忽略
【发布时间】:2018-11-11 18:25:11
【问题描述】:

在下面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 可能未使用的属性 [dcl.attr.unused]
...
2. 属性可以应用于类、typedef-name、变量、非静态数据成员、函数、枚举或枚举器的声明。
...

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

有人有什么见解吗?

【问题讨论】:

  • g++ 是什么版本,具体的编译命令是什么?
  • 编译器如何知道某个公共成员是否未被使用?
  • @BaummitAugen:对于本地类型(函数或文件范围),编译器可能知道。
  • 您是否找到了一种方法来删除 gcc 的警告,同时为您的成员变量保留 [[maybe_unused]] 属性?

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


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

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

    更正:

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

    虽然[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 都没有与“本地类的未使用的 public 字段或隐藏的类的内部链接”相关的警告。

    那么我们可以像我们自己一样为什么 Clang not 发出一个实现定义的警告,即对于非静态数据成员的情况,该属性将被忽略?原因是 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 实现的功能:

    曾经

    ...确认为增强功能。

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

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


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

    由于这里没有编译器错误需要解决,Foo 类的“可移植”(对于选择的特定编译器)实现(如果它有,比如说,(可能)未使用的私有数据成员),wrt未使用的警告,例如是使用特定于实现的编译指示,如所示,例如在@0x5453: self-answer 中,尝试对齐所选编译器的实现定义行为。

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

    【讨论】:

    • Whereas GCC does not, which would also include why GCC *(correctly)* warns the maybe_unused attribute will be ignored by it for non-static data members (even private ones), as it will simply not diagnose non-used private data members (whereas Clang does). These behaviours are both correct, as it lies in the domain of implementation-defined behaviour. 这可能是正确的,但发出该警告非常烦人且不必要。作为开发人员,我们处于无法巧妙解决的境地。
    • @Antonio 然而,一个好处是 GCC 和 Clang(根据我的经验)相互尝试在实现定义的域中具有相似的行为,例如可以看出通过我链接到的 GCC 错误报告中的 cmets。另一方面,实现定义的“错误”报告(本质上不是错误,而是更多功能/更改请求)的优先级自然低于实际错误报告,这意味着上述错误不太可能实现除非来自开源社区的人加入推动和支持变革。
    • ...如果我有更多的时间,帮助整理一些 GCC/Clang 小错误报告(例如这个)可能会很有趣和有启发性。
    • 我对这个答案并不完全满意,因为我的问题仍然没有解决。但我不会做任何事情来阻止自动奖励赏金:)
    【解决方案3】:

    GCC 不会一开始就警告你未使用的成员变量,因此该属性没有任何用途,这就是它警告你忽略它的原因。这只是一个警告,您的代码仍然合法。

    【讨论】:

    • 当同时支持两种编译器和使用-Werror 时,它会变得有点有趣。除非指定了 [[maybe_unused]],否则 Clang 会对未使用的成员发出警告,而 GCC not 会对未使用的成员发出警告,但如果指定了 [[maybe_unused]],则 发出警告。因此,如果不显式禁用其中一个编译器上的违规警告类型,就无法让该变量不使用并满足两个编译器。
    • > Clang 警告未使用的成员 struct/class 文件的匿名命名空间内声明 class 时,它才会发出警告,或者那是私人未使用的字段。例如:gcc.godbolt.org/z/WptCHY是这样吗?
    • @Youw 我遇到了未使用的私有字段的 clang 警告描述的情况
    【解决方案4】:

    现在我再次查看此内容,我无法让 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__s 是必需的,因为 MSVC 在看到 #pragma GCC 时会发出警告。

    【讨论】:

    • 请注意,当可诊断时,Clang 会针对 private 非静态数据成员发出警告,并允许使用 maybe_unused 属性来抑制这些警告。
    • 我认为你不是 100% 正确,但你让我走上了正确的道路,-Wno-ignored-attributes 可能会解决我的问题https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html。我正在发布一个单独的答案。
    • ...然而,-Wno-ignored-attributes 最终也不起作用,-Wno-attributes 是唯一达到预期输出的人
    【解决方案5】:

    请注意,以下内容,即使表面上正确,实际上也不起作用。只有更广泛的选项 -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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-23
      • 2017-06-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多