当前位置: 首页 > 知识库问答 >
问题:

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

云英才
2023-03-14

我需要一个类型特征,将枚举分解为它们的底层类型,并与所有其他类型的decation\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;

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

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

在GCC中更好一点——说第二个模板参数的声明在两个BaseType模板之间不兼容。但是根据我对SFINAE的理解,对于任何给定的T,只有一个应该是可见的,而另一个应该由于enable_if而格式错误。

戈德博尔特链接

共有3个答案

龙永福
2023-03-14

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>;
    };
}
李凯定
2023-03-14

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

你可以通过部分专业化来实现:

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

演示

钱黎明
2023-03-14

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

为了让它发挥作用,我们应该重组trait实现之间的关系,使它们专门化同一个模板。

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;

专门化为第二个参数提供了一个void类型(就像主参数被实例化一样)。但因为它是以一种“特殊的方式”实现的,偏序认为它更专业。当替换失败(我们没有通过枚举)时,主节点成为后备节点。

只要第二个模板参数总是无效,并且所有专业都有相互排斥的条件,就可以提供任意数量的专业化。

 类似资料:
  • 我有以下特征来检测两种类型是否可除,并返回结果操作类型或另一种type,否则返回: 当我尝试使用不可分割的类型时,这对libstdc很有效,但对libc不起作用,例如: 我得到以下错误: /Library/Developer/CommandLineTools/usr/include/c/v1/chrono:764:81:错误:std:_1::common_type“”typename common

  • 这个问题是在泛型关联类型在Rust中可用之前提出的,尽管它们是被提出和开发的。 我的理解是,特征泛型和关联类型在它们可以绑定到结构的类型数量上有所不同。 关联类型仅绑定1个类型: 泛型关联类型是这两种类型的混合。它们绑定到一个类型,正好有一个关联的生成器,而生成器又可以关联任何数量的类型。那么前面示例中的和这个泛型关联类型有什么区别呢?

  • 假设我有一个 我可以为任何我可能想要的结构实现,例如: 现在,我想自动我的特征,无论元组是由所有实现特征的类型组成的。直觉上,所有快乐的元组也是快乐的。 有可能做这样的事吗?例如,我可以简单地将的实现扩展到两种类型的任意元组: 因此,这可以完美地编译: 但是我怎么能把它推广到任何长度的元组呢?就我的理解而言,我们在Rust中没有变异的泛型。有变通办法吗?

  • 我希望有一个类型特征,对于任何在使用前不需要内存初始化的类型返回true,并且其复制构造函数可以作为memcpy实现。 我想让它回到真实 < li >整数类型(char、short int、int、long int等) < li >浮点数类型(float,double) < li>il::array (il::array是我自己对std::array的实现)for T是int、double、il:

  • 我想要一个方法,用于、和,因此我创建了一个trait: 我得到一个错误: 根据Rust标准库文档,未实现。为什么存在冲突的实现?

  • 我正在尝试实现BST,但是我的树的head值每次都返回无。我尝试在Python中查找其他实现,但它们通常只是声明一个根并将其传递到类本身之外,而不是将head自包含在类中。