【发布时间】:2016-03-21 03:53:22
【问题描述】:
在我们的项目中,我们使用了很多“使用”来明确说明变量应该代表什么。它主要用于std::string 标识符,如PortalId 或CakeId。现在我们目前能做的是
using PortalId = std::string;
using CakeId = std::string;
PortalId portal_id("2");
CakeId cake_id("is a lie");
portal_id = cake_id; // OK
我们不喜欢。我们希望在编译期间进行类型检查,以防止我们混合使用苹果和橙子,同时保留原始对象的大部分 yum yum 方法。
所以问题是 - 这是否可以在 C++ 中完成,这样使用将接近以下内容,分配会失败,我们仍然可以将它与地图和其他容器一起使用?
SAFE_TYPEDEF(std::string, PortalId);
SAFE_TYPEDEF(std::string, CakeId);
int main()
{
PortalId portal_id("2");
CakeId cake_id("is a lie");
std::map<CakeId, PortalId> p_to_cake; // OK
p_to_cake[cake_id] = portal_id; // OK
p_to_cake[portal_id] = cake_id; // COMPILER ERROR
portal_id = cake_id; // COMPILER ERROR
portal_id = "1.0"; // COMPILER ERROR
portal_id = PortalId("42"); // OK
return 0;
}
我们已经尝试过将宏与模板结合使用,但并没有完全得到我们需要的东西。并且添加 - 我们可以使用 c++17。
编辑:我们想出的代码是
#define SAFE_TYPEDEF(Base, name) \
class name : public Base { \
public: \
template <class... Args> \
explicit name (Args... args) : Base(args...) {} \
const Base& raw() const { return *this; } \
};
这是丑陋的并且不起作用。而且它不起作用我的意思是编译器可以使用。portal_id = cake_id;
EDIT2: 添加了explicit 关键字,我们的代码实际上可以很好地用于我们的示例。不确定这是否是正确的方法以及是否涵盖所有不幸的情况。
【问题讨论】:
-
只需在 c-tor 之前添加显式即可。
-
如果你对字符串这样做,使用该类型很容易导致未定义的行为:
SAFE_TYPEDEF(std::string, S); std::string* s = new S(); delete s;std::string不打算用作基类。 -
portal_id = cake_id有效,因为有了这个构造,CakeID可以传递到任何需要字符串的地方——例如传递给PortalId的赋值运算符。继承定义了一种“是一种”关系。 -
使用隐含定义操作的简洁语法定义新类型的问题在于,并非所有操作都与这些不同类型相关,具体取决于只能由程序员详细描述的意图。