c++ class 模板部分特化而不特化所有成员函数

c++ class template partial specialization without specializing all member functions

假设我有一个 class X<'T, N'>,其中 定义了几个函数 我还希望 class X<'T, 3'> 拥有 全部 ,还有 a一些附加函数 (现实生活中的例子是 Vector<'T, N'> 并且 Vector<'T, 3'> 将具有所有 Vector<'T, N'> 函数, 但也可以实现 3D 旋转 space)

template <class T, size_t N>
class X {
public:
    void a();
    X<T, N> b(X<T, N> x);
    // etc
    static X<T, n> c();
protected: 
    T data[N];
};

1) 部分专业化方法

template <class T>
class X<T, 3> {
public:
    void d();
};

template <class T>
using X3 = X<T, 3>;

a) [Y] std::hash<'X3<'T'>'>是一样的作为 std::hash<'X<'T, 3'>'> 所以如果我为 X<'T, N'>[=105 重载散列=], X3<'T'> 有效
b) [Y] 函数 d 可访问的
c) [N] 函数 a、b、c 不可访问
d) [Y/N] X3<'T'> x = X3<'T'>::c();如果 caccesible

会工作

2) 继承方法

template <class T>
class X3 : public X<T, 3> {
public:
    void d();
};

a) [N] std::hash<'X3<'T'>'> 不是与 std::hash<'X<'T, 3'>'> 相同,所以如果我为 X<'T, N'>[= 重载哈希105=], X3<'T'> 不适用
b) [Y] 函数 daccesible
c) [Y/N] 函数 a、b、c 可访问 ,但是 bc 有类似的 cast issues (详见下文)
d) [N] X3<'T'> x = X3<'T'>:: c(); 不会工作,即使可访问

解释:
编译器知道如何将 X3<'T'> 转换为 X<'T, 3'> (所以通过X3<'T'> 作为函数 b 的参数有效)
编译器不知道如何将 X<'T, 3'> 转换为 X3<'T'> (因此将函数 bc 结果分配给 X3<'T'> 变量不起作用)

(设法通过创建移动构造函数'X3(X<'T, 3'>&& x) noexcept'绕过它,但std::hash问题仍然存在于这种情况下)

有没有办法结合这两种方法的优点?
(除了使用方法 1)并像这样重写所有方法:)

//...

X3<T> b(X3<T> x) {
   return X<T, 3>::b(x);
}

//...  

(或方法 2)和奇怪的转换,move/copy 构造函数,并且必须专门针对 std::hash 之类的模板几次)

根据我的经验,当您确实想要从模板继承的新 class 时,请使用继承方法 (2)。当偏特化与一般情况有很大不同或根本没有定义一般情况时使用方法 (1),您只需声明一堆偏特化即可使用 SFINAE。

如果您只想 enable/disable 基于模板参数的 class 方法,请使用 SFINAE。通过这种方式,您可以将所有定义集中在一个地方并更轻松地对它们进行推理。这是首选方法,除非您希望用户自己制作模板专业化 - 这通常是不可取的。

SFINAE 示例:

template <class T, size_t N>
class X {
public:
    void a() {...};
    X<T, N> b(X<T, N> x);
    // etc
    static X<T, n> c() {...};

    template<size_t uN = N, std::enable_if_t<uN==3,int>=0>
    void d() {...};
protected: 
    T data[N];
};

此处,X<int,3> x; x.d(); 会编译,但 X<int,2> x; x.d(); 不会,因为 d() 方法被禁用,除非 N==3.

您可以在众多在线指南中阅读更多关于 SFINAE 在模板函数和 classes 中的用法(请注意,函数和 classes 的语法完全不同)。