使用SFINAE部分特化而不触及主模板

iba*_*bab 5 c++ templates partial-specialization sfinae

我正在尝试使用SFINAE专门为多种类型的结构模板.我知道类似以下的东西:

#include <iostream>

template <typename T, typename Enable = void>
struct S {
    void operator()() {
        std::cout << "Instantiated generic case" << std::endl;
    }
};

template<typename T>
using enabled_type = typename std::enable_if<
                         std::is_same<T, int>::value ||
                         std::is_same<T, float>::value
                     >::type;

template <typename T>
struct S<T, enabled_type<T>> {
    void operator()() {
        std::cout << "Instantiated int/float case" << std::endl;
    }
};

int main() {
    S<float>()();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我的问题是我无法修改S 要添加的结构的主模板typename Enable = void,因为它是外部标头库的一部分.所以主模板必须如下所示:

template <typename T>
struct S {
    void operator()() {
        std::cout << "Instantiated generic case" << std::endl;
    }
};
Run Code Online (Sandbox Code Playgroud)

有没有办法让我仍然可以使用SFINAE来专门化这个模板?

编辑:请注意,S结构由外部库中的代码使用,因此我必须实际专门化S并且不能将其子类化.此外,我正在处理的真实代码要复杂得多,并且从SFINAE中获益远远超过这个简单的例子(我有多个模板参数需要专门针对多种类型的所有组合).

Nir*_*man 4

我有一个坏消息要告诉你:你想要的东西是不可能的。如果主模板没有默认用于void执行操作的额外模板参数enable_if,则无法从最一般的意义上执行此操作。最接近的方法是专门化模板类本身的结构,换句话说:

template <class T>
struct foo {};

template <class T>
struct S<foo<T>> {};
Run Code Online (Sandbox Code Playgroud)

这会起作用。但显然,这不会产生与专门化某些东西(当它与特征匹配时)相同的灵活性。

std::hash您的问题实际上完全等同于尝试专门化满足特征的任何类型的问题。就像您的问题一样,主要类模板定义无法更改,因为它在库代码中,并且库代码实际上hash在某些情况下在内部自动使用专业化,因此无法真正定义新的模板类。

您可以在这里看到类似的问题:Specializing std::hash to衍生类。从类继承是一个很好的例子,它可以表示为特征,但不能表示为模板类。一些知识渊博的 C++ 人员注意到了这个问题,没有人提供比 OP 编写宏来自动消除专业化的解决方案更好的答案。不幸的是,我认为这对您来说也是最好的解决方案。

回想起来,hash也许应该使用第二个模板参数进行声明,该参数默认为 void,以在其他情况下以最小的开销支持此用例。我可以发誓我什至在某个地方看到过关于这个问题的讨论,但我无法追踪它。对于您的库代码,您可能会尝试提出问题以让他们更改。这似乎不是一个重大变化,即:

template <class T, class = void>
struct S {};

template <>
struct S<double> {};
Run Code Online (Sandbox Code Playgroud)

似乎是有效的。