考虑以下类定义:
// 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)
}
}
}
只要枚举的声明包含在类中就可以。
例如,如果我像下面所示的那样更改类定义,它就不再工作了:
// 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.BAR
是undefined
。
最基本的问题是:为什么使用声明的不能工作?
请注意,它适用于转发声明,例如:
// 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_ENUM
的文档实际上是真实的(强调我的):
这个宏向元对象系统注册一个枚举类型。必须放在具有Q_OBJECT或Q_GADGET宏的类的枚举声明之后。
整节没有提到定义。
另一边,我们有这个来自标准:
using声明将一组声明引入using声明所在的声明区域。
所以,我希望它也能工作。无论如何,这可能是我的一个错误的期望。
话虽如此,对于如何处理这种不方便有什么建议吗?
在枚举在类外定义的情况下,我能看到的解决它的唯一方法是在类内定义另一个枚举,并在它们之间具有一对一的映射。
这与标准c++没有任何关系,至少没有直接关系。Qt的moc在理解c++语法规则方面相当有限(这是有充分理由的1)。显然,这种将枚举导入类作用域的方式超出了它的能力范围。
在我的代码中,当我想让moc为我生成enum <->字符串转换时,我使用别名,但方向相反:
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++解析器的实现成本是非常不经济的。