【问题标题】:Making scoped enumerations comparable to underlying type使作用域枚举与基础类型具有可比性
【发布时间】:2013-03-17 11:37:55
【问题描述】:

我正在尝试使我的程序中的范围枚举与基础类型相当,但以下代码不起作用。是因为我正在使用的编译器(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;

【问题讨论】:

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


    【解决方案1】:

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

    #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

    【讨论】:

    • 完全没有,因为我并没有要求针对特定编译器中缺乏 C++11 支持的可能解决方法,而是要求确认我的代码是否符合 C++11 或不是(你的代码无论如何都不能在 VC11 中工作,因为 VC11 不支持函数模板的默认模板参数)。但是您对 SFINAE 的提及确实帮助我更好地理解了我自己编写的代码 :) 现在我认为它应该符合 C++11 标准(尽管仍然需要有知识的人确认),以及它不起作用的原因VC11 是因为它缺乏对表达式 SFINAE 的支持。
    • @PowerGamer:您的代码无法在任何编译器上运行(see here 说服),这正是我在之前的 cmets 中解释的原因(我删除了,因为您删除了对澄清)。这与缺乏支持无关,您的代码根本无法工作。可能 VC11 不支持模板参数的默认参数,这就是为什么 my 解决方案在 VC11 上不起作用的原因。我不知道。但它可以解决。让我编辑。
    • 我仍然不明白为什么我的代码不符合 C++11。编译器不应该简单地丢弃产生错误的重载模板专业化(参见我帖子中标准的引用)。另外,liveworkspace.org 在我使用的 IE10 中也不起作用。你编辑的代码在 VC11 中工作。我删除 cmets 是因为它们没有完全完成,因为我没有预期 Enter 键来提交它们。
    • @PowerGamer:如果错误是在非即时上下文中产生的,则不会,您的std::underlying_type&lt;E&gt;::type就是这种情况。这在评论中解释起来并不那么简单,但您可以阅读this Q&A 来弄清楚。
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-11-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-13
      • 1970-01-01
      • 2019-08-29
      相关资源
      最近更新 更多