使范围枚举与底层类型相当

时间:2013-03-17 11:37:56

标签: c++ templates c++11 enums sfinae

我试图在我的程序中使用与底层类型相当的范围枚举,但以下代码不起作用。是因为我使用的编译器(VC11)中的C ++ 11标准支持不佳,还是因为代码违反了C ++ 11标准中的一些规则?在后一种情况下,哪些规则正在被打破(欢迎参考具体的标准条款)?

#include <type_traits>
enum class Test: short int { A,B,C };
template<typename E> bool operator != (E e, typename std::underlying_type<E>::type n)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}
template<typename E> bool operator != (typename std::underlying_type<E>::type n, E e)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}
int main()
{
    short int x = 123;
    x != Test::B; // compilation error
}

这就是我认为我的代码应该符合C ++ 11的原因。引用C ++ 11标准(14.8.3.1):

  

对于每个函数模板,如果参数推导和检查成功,则为模板参数   (推导和/或显式)用于合成单个函数模板的声明   专门化,添加到候选函数集中以用于重载决策。如果,对于给定的函数模板,> gt;参数推导失败,则不会向该模板的&gt;候选函数集添加此类函数。

EDIT。我的代码不符合C ++ 11(感谢Vaughn Cato和Andy Prowl的解释)。替代工作代码在Andy Prowl的答案中提供。

P.S。毕竟我最终使用命名空间制作了无范围的枚举:

namespace Test_ {
    enum Test { A,B,C };
};
using Test_::Test;

namespace Test2_ {
    enum Test2 { Z,Y,B };
};
using Test2_::Test2;

2 个答案:

答案 0 :(得分:4)

当相应的参数枚举时,您可以使用SFINAE来排除比较运算符签名的实例化(以及std::underlying_type<T>的实例化):

#include <type_traits>

template<typename E, 
    typename std::enable_if<std::is_enum<E>::value>::type* = nullptr>
bool operator != (E e, typename std::underlying_type<E>::type n)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}

template<typename E, 
    typename std::enable_if<std::is_enum<E>::value>::type* = nullptr>
bool operator != (typename std::underlying_type<E>::type n, E e)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}

这是live example

修改

由于VC11似乎缺乏对函数模板模板参数的默认参数的支持,因此这是一个替代解决方案:

template<typename E>
typename std::enable_if<std::is_enum<E>::value, bool>::type
operator != (E e, typename std::underlying_type<E>::type n)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}

template<typename E>
typename std::enable_if<std::is_enum<E>::value, bool>::type
operator != (typename std::underlying_type<E>::type n, E e)
{
    return static_cast<typename std::underlying_type<E>::type>(e) != n;
}

当然是live example

答案 1 :(得分:1)

C ++ 11标准的第14.8.2节第8段规定:

  

如果替换导致无效的类型或表达式,则类型推导失败。无效的类型或表达式       如果使用替换参数编写,则会形成错误的。 [注意:访问检查完成为       替代过程的一部分。 - 结束注释] 仅在上下文中使用无效的类型和表达式       函数类型及其模板参数类型可能导致演绎失败。 [注意:评估       替换类型和表达式可能导致副作用,例如类模板的实例化       专业化和/或功能模板专业化,隐式定义函数的生成等。       这种副作用不在“直接背景”中,并且可能导致程序形成不良。 - 结束       注意]

在您的情况下,实例化underlying_type会导致失败,但不会导致直接上下文,因此它不是类型扣除失败,因此SFINAE不适用。