pae*_*bal 11 c++ migration enums c++11
我们即将(在接下来的两年内)将所有编译器迁移到C++ 11-ready编译器.
我们的客户将使用我们的标题,现在我们可以为新API编写(或多或少从头开始)标题.
因此,我们必须在保持C++ 03枚举(包括所有瑕疵)之间做出选择,或者使用包装类来模拟C++ 11表示法,因为我们最终希望将这些枚举移动到C++ 11.
下面提出的"LikeEnum"成语是否是一个可行的解决方案,还是隐藏着意外的惊喜?
template<typename def, typename inner = typename def::type>
class like_enum : public def
{
typedef inner type;
inner val;
public:
like_enum() {}
like_enum(type v) : val(v) {}
operator type () const { return val; }
friend bool operator == (const like_enum & lhs, const like_enum & rhs) { return lhs.val == rhs.val; }
friend bool operator != (const like_enum & lhs, const like_enum & rhs) { return lhs.val != rhs.val; }
friend bool operator < (const like_enum & lhs, const like_enum & rhs) { return lhs.val < rhs.val; }
friend bool operator <= (const like_enum & lhs, const like_enum & rhs) { return lhs.val <= rhs.val; }
friend bool operator > (const like_enum & lhs, const like_enum & rhs) { return lhs.val > rhs.val; }
friend bool operator >= (const like_enum & lhs, const like_enum & rhs) { return lhs.val >= rhs.val; }
};
Run Code Online (Sandbox Code Playgroud)
这将使我们能够升级我们的枚举,而无需在用户代码中进行不必要的更改:
// our code (C++03) | our code C++11
// --------------------------------------+---------------------------
|
struct KlingonType | enum class Klingon
{ | {
enum type | Qapla,
{ | Ghobe,
Qapla, | Highos
Ghobe, | } ;
Highos |
} ; |
} ; |
|
typedef like_enum<KlingonType> Klingon ; |
|
// --------------------------------------+---------------------------
// client code (both C++03 and C++11)
void foo(Klingon e)
{
switch(e)
{
case Klingon::Qapla : /* etc. */ ; break ;
default : /* etc. */ ; break ;
}
}
Run Code Online (Sandbox Code Playgroud)
注意:LikeEnum的灵感来自Type Safe Enum成语
注2:源兼容性不包括编译错误,因为隐式转换为int:这些被认为是不合需要的,并且将提前通知客户端进行显式的整数转换.
简短的回答是肯定的,这是一个可行的解决方案(只需一次修复).
这是一个很长的答案.:)
严格来说,您的比较函数存在编译时错误.这将导致符合标准的编译器出现可移植性问题.特别要考虑以下因素:
bool foo(Klingon e) { return e == Klingon::Qapla }
Run Code Online (Sandbox Code Playgroud)
编译器不应该知道operator==
要使用哪个重载,因为转换e
为KlingonType::type
隐式(via operator type() const
)和转换Klingon::Qapla
为Klingon
隐式(via Klingon(type)
)都需要一次转换.
要求operator type() const
是explicit
将修复这个错误.当然,explicit
在C++ 03中不存在.这意味着你必须按照@Yakk在评论中的建议去做,并使用类似于inner
类型的安全布尔风格的东西.operator type() const
完全删除不是一种选择,因为它会删除对整数类型的显式转换.
既然你说你仍然可以进行隐式转换,那么更简单的解决方法是定义具有底层enum
类型的比较函数.所以除了:
friend bool operator == (const like_enum & lhs, const like_enum & rhs) { return lhs.val == rhs.val; }
friend bool operator != (const like_enum & lhs, const like_enum & rhs) { return lhs.val != rhs.val; }
friend bool operator < (const like_enum & lhs, const like_enum & rhs) { return lhs.val < rhs.val; }
friend bool operator <= (const like_enum & lhs, const like_enum & rhs) { return lhs.val <= rhs.val; }
friend bool operator > (const like_enum & lhs, const like_enum & rhs) { return lhs.val > rhs.val; }
friend bool operator >= (const like_enum & lhs, const like_enum & rhs) { return lhs.val >= rhs.val; }
Run Code Online (Sandbox Code Playgroud)
你还需要:
friend bool operator ==(const like_enum& lhs, const type rhs) { return lhs.val == rhs; }
friend bool operator !=(const like_enum& lhs, const type rhs) { return lhs.val != rhs; }
friend bool operator < (const like_enum& lhs, const type rhs) { return lhs.val < rhs; }
friend bool operator <=(const like_enum& lhs, const type rhs) { return lhs.val <= rhs; }
friend bool operator > (const like_enum& lhs, const type rhs) { return lhs.val > rhs; }
friend bool operator >=(const like_enum& lhs, const type rhs) { return lhs.val >= rhs; }
friend bool operator ==(const type lhs, const like_enum& rhs) { return operator==(rhs, lhs); }
friend bool operator !=(const type lhs, const like_enum& rhs) { return operator!=(rhs, lhs); }
friend bool operator < (const type lhs, const like_enum& rhs) { return operator> (rhs, lhs); }
friend bool operator <=(const type lhs, const like_enum& rhs) { return operator>=(rhs, lhs); }
friend bool operator > (const type lhs, const like_enum& rhs) { return operator< (rhs, lhs); }
friend bool operator >=(const type lhs, const like_enum& rhs) { return operator<=(rhs, lhs); }
Run Code Online (Sandbox Code Playgroud)
在修复上述内容之后,语义上几乎没有明显的差异(忽略隐式转换是可能的).我发现的唯一区别是C++ 11 中std::is_pod<Klingon>::value
from 的值<type_traits>
.使用C++ 03版本,这将是false
,而使用enum class
es,这将是true
.在实践中,这意味着(没有优化)可以在寄存器中携带Klingon
使用enum class
,而like_enum
版本将需要在堆栈上.
因为你没有指定的底层表示enum class
,sizeof(Klingon)
将可能是两个相同的,但我不会依赖于它.不同实现所选择的底层表示的不可靠性enum
毕竟是强类型s 背后的动机的一部分.
以下是 clang ++ 3.0 +,g ++ 4.5+和msvc 11+的上述两段证明.
现在就编译输出而言,两者显然都会有不兼容的ABI.这意味着您的整个代码库需要使用其中一个或另一个.他们不会混在一起.对于我的系统(OSX上的clang ++ - 3.5),上面的函数符号__Z1f9like_enumI11KlingonTypeNS0_4typeEE
用于C++ 03版本和__Z1f7Klingon
C++ 11版本.如果这些是导出的库函数,这应该只是一个问题.
在将优化转换-O2
为我的clang ++和g ++测试后,输出的程序集是相同的.想必其他优化编译器也可以解开Klingon
来KlingonType::type
.如果没有优化,enum class
版本仍然会避免所有构造函数和比较运算符函数调用.