c++ 成员函数模板专门化(在类定义之外定义函数)

x759pob2  于 2023-04-01  发布在  其他
关注(0)|答案(1)|浏览(98)

我一直在寻找解决我的问题超过堆栈溢出,并看到许多类似的主题,但没有答案指向如此具体的情况。
在附加的代码中,除非我取消注解#define ISSUE,否则一切正常,然后我会得到很多错误。
这里的目标是能够只在类体中声明专门化。实现需要在类定义之后放在同一个文件中。
为什么这不起作用?如何使它起作用?
我的编译器支持C++17,没有更新。

#include <cstdint>
#include <iostream>
#include <type_traits>

// #define ISSUE

template <typename T>
class Test
{
  public:
    template<typename U = T,
        std::enable_if_t<
            std::is_same<T,     bool>::value ||
            std::is_same<T,   int8_t>::value ||
            std::is_same<T,  uint8_t>::value ||
            (std::is_enum<T>::value && (sizeof(T) == 1U))
        ,int> = 0>
    static void special(T data);

#if defined(ISSUE)
    template<typename U = T,
        std::enable_if_t<
            std::is_same<T,  int32_t>::value ||
            std::is_same<T, uint32_t>::value ||
            (std::is_enum<T>::value && (sizeof(T) == 4U))
        ,int> = 0>
    static void special(T data);
#endif    
};

template <typename T>
template <typename, 
        std::enable_if_t<
            std::is_same<T,     bool>::value ||
            std::is_same<T,   int8_t>::value ||
            std::is_same<T,  uint8_t>::value ||
            (std::is_enum<T>::value && (sizeof(T) == 1U))
        ,int>>
void Test<T>::special(T data)
{
    std::cout << "print 8-bit\n";
}

#if defined(ISSUE)
template <typename T>
template <typename, 
        std::enable_if_t<
            std::is_same<T,  int32_t>::value ||
            std::is_same<T, uint32_t>::value ||
            (std::is_enum<T>::value && (sizeof(T) == 4U))
        ,int>>
void Test<T>::special(T data)
{
    std::cout << "print 32-bit\n";
}
#endif    

int main()
{
    Test<uint8_t>{}.special(5);
    Test<int8_t>{}.special(5);
    Test<bool>{}.special(true);
#if defined(ISSUE)
    Test<uint32_t>{}.special(5);
    Test<int32_t>{}.special(5);
#endif    
}

仅限帖子:
How to use std::enable_if on method of templated class with seperate declaration and definition via specialization
帮助了一点,得到了什么与#define ISSUE注解了。

5sxhfpxr

5sxhfpxr1#

为什么这不起作用?
首先,它们只是模板函数重载,而不是成员函数的特殊化。
SFINAE只能在std::enable_if_t的条件依赖于函数的模板参数类型时工作。这意味着,在您显示的代码中,它必须依赖于U,而不是类模板参数T
除此之外,std::enable_if_t的第二个参数是函数的返回类型,它将是SFINAEd。您已经提供了int作为返回,它必须是void(或者不提供,因为它是std::enable_if_t的默认类型)。
如何让它发挥作用?
我提出了另一种方法,直接在函数签名中使用std::enable_if_t作为返回类型,而不是在模板参数列表中。
你仍然可以把函数的定义单独保存在类之外。

template <typename T>
class Test
{
public:
    template<typename U = T>
    std::enable_if_t<
        std::is_same<U, bool>::value ||
        std::is_same<U, int8_t>::value ||
        std::is_same<U, uint8_t>::value ||
        (std::is_enum<U>::value && (sizeof(U) == 1U))
    > special(T data);

    template<typename U = T>
    std::enable_if_t<
        std::is_same<U, int32_t>::value ||
        std::is_same<U, uint32_t>::value ||
        (std::is_enum<U>::value && (sizeof(U) == 4U))
    > special(T data);
};

See live demo godbolt.org
话虽如此,由于您可以访问C++17编译器,因此可以查看if constexpr,这是该场景的更好替代方案。或者使用fold expression,您也可以简化扩展的std::enable_if条件,如下所示:

// alias std::enable_if for type checking
template <std::size_t N, typename U, typename... Ts>
using enabled_types = std::enable_if_t<
    (std::is_same_v<U, Ts> || ...) ||
    (std::is_enum_v<U> && (sizeof(U) == N))
 >;

template <typename T>
class Test
{
public:
    template<typename U = T>
    enabled_types<1U, U, bool, int8_t, uint8_t> special(T data);

    template<typename U = T>
    enabled_types<4U, U, int32_t, uint32_t> special(T data);
};

See live demo godbolt.org

相关问题