使作用域枚举可与基础类型比较

4

我正在尝试使我的程序中的作用域枚举可比较底层类型,但以下代码不起作用。这是因为我使用的编译器(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):
对于每个函数模板,如果参数推导和检查成功,则使用模板参数(推导的和/或显式的)来综合声明单个函数模板专业化,该专业化添加到候选函数集以用于重载解析。 如果给定函数模板的参数推导失败,则不会将此类函数添加到该模板的候选函数集中。
编辑。 我的代码不符合C++11标准(感谢Vaughn Cato和Andy Prowl的解释)。 Andy Prowl的答案提供了替代的可行代码。
附注。最终,我使用命名空间使非作用域枚举变得有作用域:
namespace Test_ {
    enum Test { A,B,C };
};
using Test_::Test;

namespace Test2_ {
    enum Test2 { Z,Y,B };
};
using Test2_::Test2;
2个回答

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;
}

这是一个实时示例
编辑:
由于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;
}

And a live example of course.


完全不是这样,因为我并没有要求在特定编译器中解决C++11支持不足的问题,而是要确认我的代码是否符合C++11标准(无论如何,您的代码在VC11中都无法工作,因为VC11缺乏函数模板的默认模板参数支持)。但是,您提到的SFINAE确实帮助我更好地理解了我自己编写的代码 :) 现在我认为它应该符合C++11标准(尽管仍然希望得到有经验的人的确认),而它不能在VC11中工作是因为它缺乏对表达式SFINAE的支持。 - PowerGamer
@PowerGamer:你的代码在任何编译器上都无法工作(点击此处查看),正是我在之前的评论中解释的原因(由于你删除了请求澄清的内容,我也删除了评论)。这不是缺乏支持的问题,你的代码根本就无法工作。可能VC11在模板参数的默认参数方面缺乏支持,这就是为什么我的解决方案在VC11上无法工作的原因。我不知道那一点。但是可以通过解决方法来解决。让我编辑一下。 - Andy Prowl
1
我仍然不明白为什么我的代码不符合C++11标准。编译器难道不应该简单地丢弃产生错误的重载模板特化吗(请参见我帖子中标准的引用)?此外,liveworkspace.org在我使用的IE10中无法工作。而你编辑过的代码在VC11中可以工作。我删除注释是因为它们没有完全完成,因为我没有预料到回车键会提交它们。 - PowerGamer
1
@PowerGamer:如果错误是在非直接上下文中产生的,那么就不是你的std::underlying_type<E>::type的情况。这不是在评论中简单解释的,但你可以阅读这个问答来理解它。 - Andy Prowl

1
C++11标准的第14.8.2节第8段规定:如果替换导致无效类型或表达式,则类型推断失败。无效类型或表达式是指使用替换的参数编写将会出错的类型或表达式。[注意:访问检查作为替换过程的一部分完成。-注] 仅函数类型及其模板参数类型的直接上下文中的无效类型和表达式才会导致推断失败。[注意:替换类型和表达式的评估可能导致副作用,例如类模板专业化的实例化和/或函数模板专业化的生成,隐含定义函数等。这些副作用不在“直接上下文”中,可能导致程序出错。-注]
在您的情况下,实例化underlying_type导致失败,但不在直接上下文中,因此它不是类型推断失败,因此不适用SFINAE。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接