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

时间:2016-08-09 20:23:32

标签: c++ templates sfinae partial-specialization

我试图一次性使用多种类型的结构模板 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;
}

我的问题是我无法修改S的主要模板 struct添加typename Enable = void,因为它只是外部标题的一部分 图书馆。所以主模板必须如下所示:

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

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

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

2 个答案:

答案 0 :(得分:2)

你确定简单的旧专业化还不够吗?

struct S_int_or_float {
    void operator()() {
        std::cout << "Instantiated int/float case" << std::endl;
    }
};

template<> struct S<int>: S_int_or_float {};
template<> struct S<float>: S_int_or_float {};

修改 你说你必须提供参数的组合...所以至少有两个参数...让我们看看我们能做些什么:

struct ParentS { /* some implementation */ }; 

template <class First, class Second>
struct S<First, Second, enable_if_t<trait_for_first_and_second<First, Second>::value, first_accepted_type_of_third_parameter> >: ParentS { };

template <class First, class Second>
struct S<First, Second, enable_if_t<trait_for_first_and_second<First, Second>::value, second_accepted_type_of_third_parameter> >: ParentS { };

// ...

它不像SFINAE那样会有额外的参数,但仍然没有指数......

答案 1 :(得分:2)

我有个坏消息:你想要的是不可能的。如果主模板没有额外的模板参数,默认为void以进行enable_if,那么就没有办法在最普遍的意义上做到这一点。你最接近的是专门为模板类本身构造结构,换句话说:

template <class T>
struct foo {};

template <class T>
struct S<foo<T>> {};

这会奏效。但显然,如果匹配特征,这不会产生与专业化相同的灵活性。

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

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

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

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

template <>
struct S<double> {};

似乎有效。