c++ - 为什么方法重载或枚举标志定义会触发 gcc7.2 编译器警告?

标签 c++ qt gcc enums overloading

在 GCC 7.2(但不是 4.4.7!)中编译时,生成以下代码 “ bool 上下文中的枚举常量”(-Wint-in-bool-context) 警告。如果我执行以下任一操作,警告就会消失:

  • 我删除了另一个重载 (void func(bool))
  • 我删除了枚举中的 FOOBAZ 组合并在函数调用中执行按位或运算 (flagTest.func(Test::FOO | Test::BAZ) ).

我的问题是:

  • 为什么这些更改中的任何一个都会删除警告。和/或为什么首先触发它?
  • 除上述之外,我是否可以进行任何简单的更改来修复警告?

在我的实际问题的上下文中,我无法删除重载,并且 FOOBAZ 等价物被多次使用(类似于 Qt::AlignCenter )。我已经做了很多搜索,但如果我在 SO 或其他地方找到任何东西,我就会忘记它的相关性。

#include <QFlags>

class Test
{
public:
   enum SimpleFlag
   {
      FOO = 1 << 0,
      BAR = 1 << 1,
      BAZ = 1 << 2,

      FOOBAZ = (FOO | BAZ)
   };
   Q_DECLARE_FLAGS(MyFlags, SimpleFlag)

   Test(){}

   void func(bool) { }
   void func(Test::MyFlags) { }
};

Q_DECLARE_OPERATORS_FOR_FLAGS(Test::MyFlags)

int main(int argc, char *argv[])
{
   Q_UNUSED(argc); Q_UNUSED(argv);

   Test flagTest;
   flagTest.func(Test::FOOBAZ);    
}

最佳答案

这个问题提出了一些与重载选择和隐式转换有关的有趣问题。


首先是什么Q_DECLARE_FLAGS做?来自Qt documentation ,这个宏扩展为,在这种情况下:

typedef QFlags<SimpleFlag> MyFlags;

所以 MyFlagsQFlags 的特定实例Qt 提供的类模板。

案例 1:func() 的两个版本和 FOOBAZ可用

当编译器遇到对 func(Test::FOOBAZ) 的调用时它采用 FOOBAZ 的类型,并尝试将其与函数重载相匹配。有两个功能可用。

FOOBAZ 的类型是SimpleFlag ,这是一个枚举。 SimpleFlag隐式转换为 int , 可以转换为 bool . SimpleFlag也隐式转换为 MyFlags因为QFlags有一个converting constructor .在过载选择期间,转换为 bool优于使用“用户定义”构造函数的转换(在 QFlags 中)。

编译器选择func(bool) , 然后警告 int被用作 bool .

情况2:只有func(Test::MyFlags)可用 FOOBAZ

func(bool) 外,过载解决方案与以前一样进行不可用。编译器选择 func(MyFlags) .没有警告,因为没有像 bool 值那样使用整数。

案例 3:func() 的两个版本可用,但 FOOBAZ不可用

来电是func(Test::FOO | Test::BAZ) .两者 FOOBAZ有类型 SimpleFlag .在查找哪个func()之前要使用,编译器会搜索 operator| 的重载.根据Qt documentation对于 Q_DECLARE_OPERATORS_FOR_FLAGS , 宏定义 operator|对于 Test::MyFlags .但是,在我的 Qt 5.10.0 拷贝中,它实际上定义了(在 qflags.h 中)

QFlags<Test::SimpleFlag> operator|(Test::SimpleFlag, Test::SimpleFlag);
QFlags<Test::SimpleFlag> operator|(Test::SimpleFlag, QFlags<Test::SimpleFlag>);

operator| 的所有可用重载中, 两个参数都是 Test::SimpleFlag 的那个不需要类型转换。编译器选择这个。

结果是一个 QFlags<Test::SimpleFlag> 类型的临时对象,也称为 MyFlags .然后编译器必须选择一个版本 func()基于 MyFlags 类型的参数.它选择 func(MyFlags)因为不需要类型转换。

没有发出警告,因为 int尚未在 bool 中使用上下文。


如何修复

作用域枚举 ( enum class ) 在 C++11 中引入,部分是为了防止在枚举器 ( FOOBAZ 等) 以不希望的方式转换为整数时出现此类问题。不幸的是,至少有 one problem其中 QFlags与范围枚举不兼容,至少在 QObject 范围内.

以下示例将避免选择 bool func 的版本, 但可能需要进行一些调整或返工才能使其在现实世界中与 Qt 一起工作。 static constexpr变量重新引入 FOOBAZ作为 Test 类范围内的标识符.该示例在 GCC 6.4.0 上编译(它似乎也缺少 bool 警告)。

#include <QFlags>

class Test
{
public:
    enum class SimpleFlag
    {
        FOO = 1 << 0,
        BAR = 1 << 1,
        BAZ = 1 << 2,

        FOOBAZ = (FOO | BAZ)
    };
    Q_DECLARE_FLAGS(MyFlags, SimpleFlag)

    static constexpr SimpleFlag FOOBAZ = SimpleFlag::FOOBAZ;
    Test(){}

    void func(bool) { }
    void func(Test::MyFlags) { }
};

Q_DECLARE_OPERATORS_FOR_FLAGS(Test::MyFlags)

int main(int argc, char *argv[])
{
    Q_UNUSED(argc); Q_UNUSED(argv);

    Test flagTest;
    flagTest.func(Test::FOOBAZ);    
}

overload resolution 上的这些文章和 implicit conversion对准备这个答案很有帮助。

关于c++ - 为什么方法重载或枚举标志定义会触发 gcc7.2 编译器警告?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49951267/

相关文章:

c++ - 绘制具有多纹理的对象和具有单一纹理的其他对象

c++ - 信号/槽连接总数?

c++ - Qt字符串问题

linux - 旧的 ARM32 二进制文件可以在 AARCH64 内核上运行吗?

android - 通过 MoSync 进行 android c++ 软件开发是否值得?

c++ - 无法在 VS Code 上使用代码运行器运行 C++ 文件

c++ - 通过套接字发送文件太慢

c++ - Qt Installer Framework - 读取下载的包版本

gcc - ARMv8 A64 程序集中的立即数范围

c - 什么定义了类型的大小?