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.BAR
是 undefined
。
最基本的问题是:为什么它不适用于 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_ENUM
的 documentation 实际上是正确的(强调我的):
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++ 解析器的实现成本将非常不经济。
考虑以下 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.BAR
是 undefined
。
最基本的问题是:为什么它不适用于 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_ENUM
的 documentation 实际上是正确的(强调我的):
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++ 解析器的实现成本将非常不经济。