当尝试实现类型特征时,我的 SFINAE 应用程序有什么问题?

Vio*_*ffe 16 c++ sfinae type-traits c++17

decay_t我需要一个类型特征,将枚举衰减为其基础类型,并且与所有其他类型的工作方式相同。我编写了以下代码,显然这不是 SFINAE 的工作方式。但这就是我认为它应该工作的方式,那么这段代码到底有什么问题以及我对 C++ 的理解有何差距?

namespace detail {
    template <typename T, std::enable_if_t<!std::is_enum_v<T>>* = nullptr>
    struct BaseType {
        using Type = std::decay_t<T>;
    };

    template <typename T, std::enable_if_t<std::is_enum_v<T>>* = nullptr>
    struct BaseType {
        using Type = std::underlying_type_t<T>;
    };
}

template <class T>
using base_type_t = typename detail::BaseType<T>::Type;
Run Code Online (Sandbox Code Playgroud)

MSVC中的错误完全无法理解:

“detail::BaseType”:模板参数“__formal”与声明不兼容

在 GCC 中,情况要好一些 - 表示第二个模板参数的声明在两个BaseType模板之间不兼容。但根据我对 SFINAE 的理解,对于任何给定的 T 来说,只有一个应该可见,而另一个应该由于enable_if.

神箭链接

Sto*_*ica 19

应用于类模板的 SFINAE 主要是在模板的部分特化之间进行选择。您的代码片段中的问题是看不到部分专业化。您定义两个具有相同模板名称的主类模板。这是一个重新定义错误。

为了使其发挥作用,我们应该重新构建特征实现之间的关系,例如它们专门化相同的模板。

namespace detail {
    template <typename T, typename = void> // Non specialised case
    struct BaseType {
        using Type = std::decay_t<T>;
    };

    template <typename T>
    struct BaseType<T, std::enable_if_t<std::is_enum_v<T>>> {
        using Type = std::underlying_type_t<T>;
    };
}

template <class T>
using base_type_t = typename detail::BaseType<T>::Type;
Run Code Online (Sandbox Code Playgroud)

专业化void为第二个参数提供了类型(就像实例化主参数一样)。但由于它以“特殊方式”进行,因此部分排序认为它更专业。当替换失败时(我们不传递枚举),主数据库将成为后备数据库。

您可以根据需要提供任意数量的此类专业化,只要第二个模板参数始终为void,并且所有专业化都具有互斥条件。


康桓瑋*_*康桓瑋 11

BaseType不是部分专业化,您只是重新声明它,并且由于非类型参数具有不同的类型,因此编译失败。你可能想做

#include <type_traits>

namespace detail {
  template <typename T, bool = std::is_enum_v<T>>
    struct BaseType;

    template <typename T>
    struct BaseType<T, false> {
      using Type = std::decay_t<T>;
    };

    template <typename T>
    struct BaseType<T, true> {
      using Type = std::underlying_type_t<T>;
    };
}
Run Code Online (Sandbox Code Playgroud)


Jar*_*d42 8

您使用不同的参数声明相同的结构,这是禁止的。

您可以通过部分专业化来做到这一点:

namespace detail {
    template <typename T, typename Enabler = void>
    struct BaseType {
        using Type = std::decay_t<T>;
    };

    template <typename E>
    struct BaseType<E, std::enable_if_t<std::is_enum_v<E>>>
    {
        using Type = std::underlying_type_t<E>;
    };
}
Run Code Online (Sandbox Code Playgroud)

演示