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) 的引用:

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

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

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

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

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

2 回答 2

4

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

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

当然还有一个活生生的例子

于 2013-03-17T11:46:40.090 回答
1

C++11 标准的第 14.8.2 节第 8 段指出:

如果替换导致无效的类型或表达式,则类型推导失败。无效类型或表达式是如果使用替换参数编写的格式错误的类型或表达式。[ 注意:访问检查是替换过程的一部分。— end note ]只有在函数类型及其模板参数类型的直接上下文中的无效类型和表达式会导致推导失败。[注意:替换类型和表达式的评估可能会导致副作用,例如类模板特化和/或函数模板特化的实例化,隐式定义函数的生成等。此类副作用不在“立即上下文”,并可能导致程序格式错误。——尾注]

In your case, instantiating underlying_type causes a failure, but not in the immediate context, so it is not a type deduction failure, so SFINAE does not apply.

于 2013-03-17T14:58:15.710 回答