Q_ENUMS 和 using 声明不能一起工作

Q_ENUMS and using declarations do not work together

考虑以下 class 定义:

// exported.hpp

#include <QObject>

class Exported: public QObject {
    Q_OBJECT

public:
    using QObject::QObject;
    enum class FOO { BAR };
    Q_ENUM(FOO)
};

以及以下 main 文件:

// main.cpp

#include <QApplication>
#include <QQmlApplicationEngine>
#include "exported.hpp"

int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    QQmlApplicationEngine engine;
    qmlRegisterType<Exported>("Package", 1, 0, "Exported");
    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    return app.exec();
}

通过这样做,我可以在 QML 中轻松访问我的枚举的命名常量。
例如:

// main.qml

import QtQuick 2.7
import QtQuick.Controls 2.0
import Package 1.0

ApplicationWindow {
    Rectangle {
        Component.onCompleted: {
            console.log(Exported.BAR)
        }
    }
}

只要枚举声明包含在 class.
中,这就有效 例如,如果我如下所示更改 class 定义,它将不再起作用:

// exported.hpp

#include <QObject>

enum class FOO { BAR };

class Exported: public QObject {
    Q_OBJECT

public:
    using QObject::QObject;
    using FOO = ::FOO;
    Q_ENUM(FOO)
};

现在,QML 文件中的 Exported.BARundefined


最基本的问题是:为什么它不适用于 using 声明
请注意,它适用于转发声明,例如:

// exported.hpp

#include <QObject>

enum class FOO { BAR };

class Exported: public QObject {
    Q_OBJECT

public:
    using QObject::QObject;
    enum class FOO;
    Q_ENUM(FOO)
    enum class FOO { BAR };
};

这对 Q_ENUMdocumentation 实际上是正确的(强调我的):

This macro registers an enum type with the meta-object system. It must be placed after the enum declaration in a class that has the Q_OBJECT or the Q_GADGET macro.

整节都没有提到定义。
另一方面,我们从标准中得到了这个:

A using-declaration introduces a set of declarations into the declarative region in which the using-declaration appears.

所以,我期待它也能正常工作。无论如何,这可能是我的错误期望。

也就是说,对于如何处理这种不便有什么建议吗?
如果 enum 是在 class 之外定义的,我能看到解决它的唯一方法是在其中定义另一个 enum class 并且它们之间有一个一对一的映射。
它远非可维护的,而且确实有点乏味。

这与标准 C++ 无关,至少不是直接相关。 Qt 的 moc 对 C++ 语法规则的理解非常有限(并且有充分的理由 1)。显然,这种将枚举导入 class 范围的方式超出了它的能力范围。

在我的代码中,当我希望 moc 为我生成枚举 <-> 字符串转换时,我使用别名,但方向相反:

class Exported: public QObject {
    Q_OBJECT

public:
    using QObject::QObject;

    enum class FOO { BAR };

    Q_ENUM(FOO)
};

using FOO = Exported::Foo;

这让 moc 开心并且是有效的 C++。缺点是您将 Exported 的定义拉入每个使用 FOO 定义的范围,并且您不能转发声明 FOO.

1 它是在 libclang 出现之前创建的,对于一些极端情况,完整的 C++ 解析器的实现成本将非常不经济。