【问题标题】:Why does a method overload or enum flag definition trigger gcc7.2 compiler warning?为什么方法重载或枚举标志定义会触发 gcc7.2 编译器警告?
【发布时间】:2018-04-21 00:34:39
【问题描述】:

在 GCC 7.2(但不是 4.4.7!)中编译时,会生成以下代码 “布尔上下文中的枚举常量”(-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);    
}

【问题讨论】:

  • 因为Test::MyFlags 不是SimpleFlag,这就是原因。摆脱所有的 Q-goo,只使用枚举,并且代码按预期编译,没有任何警告。

标签: c++ qt gcc enums overloading


【解决方案1】:

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


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

typedef QFlags<SimpleFlag> MyFlags;

所以MyFlags 是Qt 提供的QFlags 类模板的特定实例化。

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

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

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

编译器选择func(bool),然后警告int 正像bool 一样被使用。

案例 2:只有 func(Test::MyFlags) 可用于 FOOBAZ

func(bool) 不可用外,重载解决方法如前所述。编译器选择func(MyFlags)。没有警告,因为没有像布尔值那样使用整数。

案例 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&lt;Test::SimpleFlag&gt; 类型的临时对象,也称为MyFlags。然后编译器必须根据MyFlags 类型的参数选择func() 的版本。它选择func(MyFlags),因为不需要类型转换。

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


如何修复

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

下面的示例将避免选择funcbool 版本,但可能需要进行一些调整或修改以使其在现实世界中与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 resolutionimplicit conversion 上的这些文章有助于准备此答案。

【讨论】:

  • 很好的诊断。我应该在我的问题中提到我确实尝试了范围枚举(第一件事!)但遇到了(我认为)bugreports.qt.io/browse/QTBUG-47652。因此,要回答您的嵌入式问题,范围枚举和 Qt5 QFlags 存在问题。但是没想到声明了一堆static constexpr,好像可以避免这个问题!谢谢。
  • 这是一个有趣的错误报告。谢谢。我已将其中一些信息编辑到答案中。
猜你喜欢
  • 1970-01-01
  • 2012-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多