【问题标题】:"Conversion" from type to same type is causing error从类型到相同类型的“转换”导致错误
【发布时间】:2015-12-04 07:07:41
【问题描述】:

我有一个模板函数,其中枚举类型转换为其基础类型,它工作正常,但我编写了一个重载,它应该采用整数并返回自身,它给我一个错误,即 int 不是枚举类型。在我的模板中,这应该已被过滤掉。怎么了?

这是模板代码:

  template <typename TT>
  static constexpr auto get_value(TT t)
    -> typename std::enable_if<!std::is_enum<TT>::value, TT>::type
    {
      return t;
    }

  template <typename TT>
  static constexpr auto get_value(TT t)
    -> typename std::enable_if<std::is_enum<TT>::value, typename std::underlying_type<TT>::type>::type
    {
      return (typename std::underlying_type<TT>::type)t;
    }

Demo

【问题讨论】:

  • 我不知道 underlying_type 是否对 SFINAE 友好,但有一个 workaround 可以解决这个问题
  • 什么?我看到它起作用了,但是这里发生了什么使它起作用?为什么underlying_type by SFINAE 不友好?
  • std::underlying_type&lt;TT&gt;::type 的实例化被延迟,这样enable_if 可以先失败。 SFINAE 友好我的意思是任何替换失败仅在直接上下文中发生(如果它发生在 underlying_type 本身内部,则它不是 SFINAE 友好的)。
  • @PiotrSkotnicki 不,它对 SFINAE 不友好,它有一个“条件”,即参数应为枚举类型 (=> UB)。
  • @Adrian SFINAE 是 Substitution Failure Is Not An Error 的首字母缩写词,但它命名的概念是 Substitution Failure In The Immediate Context 不是错误。这限制了编译器在替换期间必须能够检查模板中的类型的深度。例如,template&lt;class T&gt; struct B : T {}; template&lt;class T&gt; B&lt;T&gt; foo(); 然后foo&lt;int&gt;不会产生替换失败在直接上下文中(基类本身可以从其他类继承,最后一个深层嵌套发生错误)。

标签: c++ c++11 metaprogramming template-meta-programming


【解决方案1】:

std::underlying_type&lt;TT&gt;::type 正在 std::enable_if 中进行评估,即使 std::is_enum&lt;TT&gt;::valuefalse,因为 false 不是错误。由于正在评估非枚举类型,因此会导致错误。如果我们将 SFINAE 移动到模板参数中,我们可以获得所需的重载并仍然返回正确的类型。

template <typename TT, typename std::enable_if<!std::is_enum<TT>::value, TT>::type* = nullptr>
static constexpr auto get_value(TT t) -> TT
{
    return t;
}

template <typename TT, typename std::enable_if<std::is_enum<TT>::value>::type* = nullptr>
static constexpr auto get_value(TT t) -> typename std::underlying_type<TT>::type
{
    return (typename std::underlying_type<TT>::type)t;
}

你可以看到它在这个 Live Example

【讨论】:

  • 所以为了确保我正确理解这一点,第二个模板参数是void* 类型的未命名参数,如果是/不是枚举类型(取决于我想要什么)导致如果不是我想要的,创建一个非实体然后会被拒绝?
  • @Adrian 如果std::enable_if 成功,则type 为空,然后我们可以为SFINAE 设置一个指向nullptr 的指针
  • @dyp 我已经编辑了答案。它现在应该解决实际原因。
【解决方案2】:

通过尝试使用不是枚举类型的T 实例化std::underlying_type&lt;T&gt;,您违反了标准对模板参数T 施加的要求:

§ 20.10.7.6 [meta.trans.other]/表 57:

       Template         |         Condition         |       Comments
------------------------+---------------------------+-----------------------
template <class T>      | T shall be an enumeration | The member typedef
struct underlying_type; | type (7.2)                | type shall name
                        |                           | the underlying type 
                        |                           | of T.

如果不喜欢任何额外的模板参数,这是一种替代方法:

template <typename TT>
static constexpr auto get_value(TT t)
    -> typename std::enable_if<!std::is_enum<TT>::value, TT>::type
{
    return t;
}

template <typename TT>
static constexpr auto get_value(TT t)
    -> typename std::enable_if<std::is_enum<TT>::value
                             , std::underlying_type<TT>
                >::type::type
{
    return (typename std::underlying_type<TT>::type)t;
}

这样,std::underlying_type&lt;TT&gt; 的实例化被推迟到std::enable_if 中的条件评估为true,因为std::enable_if&lt;B,T&gt;::type 返回的内容需要嵌套的type 定义。

DEMO

【讨论】:

  • 那么,只有当你得到std::underlying_type&lt;TT&gt;::type类型时才需要这个要求,而std::underlying_type&lt;TT&gt;本身不需要这个要求?这是为什么呢?
  • @Adrian std::underlying_type&lt;TT&gt; 仅用作类型模板参数时不会实例化
猜你喜欢
  • 2014-07-01
  • 2013-05-14
  • 1970-01-01
  • 1970-01-01
  • 2020-01-20
  • 1970-01-01
  • 1970-01-01
  • 2018-12-28
  • 1970-01-01
相关资源
最近更新 更多