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

发布于 2025-01-28 09:34:53 字数 1037 浏览 2 评论 0原文

我需要一种类型特征,使其基础类型衰落,并且与所有其他类型的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;

MSVC中的错误是完全难以理解的:

'细节:: basetype':模板参数'__formal'与 声明

In GCC it's a bit better - says that declarations of the second template parameter are incompatible between the two BaseType templates.但是,根据我对Sfinae的理解,对于任何给定的T,只能在任何给定的T中都能看到一个,另一个应符合enable_if

godbolt链接

I needed a type trait that decays enums to their underlying type, and works the same as decay_t for all other types. I've written the following code, and apparently this is not how SFINAE works. But it is how I thought it should work, so what exactly is the problem with this code and what's the gap in my understanding of 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;

The error in MSVC is completely unintelligible:

'detail::BaseType': template parameter '__formal' is incompatible with
the declaration

In GCC it's a bit better - says that declarations of the second template parameter are incompatible between the two BaseType templates. But according to my understanding of SFINAE, only one should be visible at all for any given T and the other one should be malformed thanks to enable_if.

Godbolt link

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

傲性难收 2025-02-04 09:34:53

应用于类模板的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;

该专业化为第二个参数提供了void类型(就像主体可以实例化一样)。但是由于它以“特殊方式”这样做,因此部分订购认为它更专业。当替代失败(我们不通过枚举)时,主要的是后备。

只要第二个模板参数始终是void,并且所有专业都有相互排斥的条件,就可以提供所需的尽可能多的专业化。

SFINAE applied to class templates is primarily about choosing between partial specialisations of the template. The problem in your snippet is that there are no partial specialisations in sight. You define two primary class templates, with the same template name. That's a redefinition error.

To make it work, we should restructure the relationship between the trait implementations in such as way that they specialise the same template.

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;

The specialisation provides a void type for the second argument (just like the primary would be instantiated with). But because it does so in a "special way", partial ordering considers it more specialised. When substitution fails (we don't pass an enum), the primary becomes the fallback.

You can provide as many such specialisation as you want, so long as the second template argument is always void, and all specialisations have mutually exclusive conditions.

身边 2025-02-04 09:34:53

astepype没有被部分专业化,您只是对其进行重新陈述,并且由于非类型参数具有不同的类型,因此汇编失败了。你可能想做

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

BaseType isn't being partial-specialized, you're just redeclaring it, and since the non-type parameter has a different type, the compilation fails. you might want to do

#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>;
    };
}
森林很绿却致人迷途 2025-02-04 09:34:53

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

您可以通过部分专业化来完成:

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

demo

You declare the same struct with different parameter, which is forbidden.

You can do it with partial specialization:

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

Demo

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文