在C ++中,如果要部分地专门化模板类中的单个方法,则必须专门化整个类(如Template specialization of a single method from templated class with multiple template parameters中所述)
然而,当具有多个模板参数的较大模板类中,当它们中的每一个影响单个函数时,这变得令人厌烦。使用N个参数,您需要专门化2 ^ N次!
但是,对于C ++ 11,我认为可能有更优雅的解决方案,但我不确定如何处理它。也许以某种方式enable_if
?有什么想法吗?
答案 0 :(得分:13)
除了Torsten提出的基于继承的解决方案之外,您还可以使用std::enable_if
和默认函数模板参数来启用/禁用该函数的某些特化。
例如:
template<typename T>
struct comparer
{
template<typename U = T ,
typename std::enable_if<std::is_floating_point<U>::value>::type* = nullptr>
bool operator()( U lhs , U rhs )
{
return /* floating-point precision aware comparison */;
}
template<typename U = T ,
typename std::enable_if<!std::is_floating_point<U>::value>::type* = nullptr>
bool operator()( U lhs , U rhs )
{
return lhs == rhs;
}
};
我们利用SFINAE来禁用/启用函数的不同“特化”,具体取决于模板参数。因为SFINAE只能依赖于函数参数而不是类参数,所以我们需要一个函数的可选模板参数,它接受类的参数。
我更喜欢这种解决方案,而不是基于继承,因为:
但是对于没有实现可选功能模板参数的编译器(如VS2012中的MSVC),此解决方案不起作用,您应该使用基于继承的解决方案。
编辑:您可以使用其他功能委托工作来覆盖模板函数的非实现的default-function-template-parameters:
template<typename T>
struct foo
{
private:
template<typename U>
void f()
{
...
}
public:
void g()
{
f<T>();
}
};
当然,编译器可以轻松地内联g()
丢弃包装调用,因此这种替代方案没有性能损失。
答案 1 :(得分:5)
一个解决方案是从函数转发,你想重载一些依赖于类模板参数的实现:
template < typename T >
struct foo {
void f();
};
template < typename T >
struct f_impl {
static void impl()
{
// default implementation
}
};
template <>
struct f_impl<int> {
static void impl()
{
// special int implementation
}
};
template < typename T >
void foo< T >::f()
{
f_impl< T >::impl();
}
或者只使用私有函数,使用template参数调用它们并重载它们。
template < typename T >
class foo {
public:
void f()
{
impl(T());
}
private:
template < typename G >
void impl( const G& );
void impl( int );
};
或者,如果它只是一种非常特殊的特殊情况,只需在实现中查询该类型。
答案 2 :(得分:4)
使用enable_if:
#include <iostream>
#include <type_traits>
template <typename T>
class A {
private:
template <typename U>
static typename std::enable_if<std::is_same<U, char>::value, char>::type
g() {
std::cout << "char\n";
return char();
}
template <typename U>
static typename std::enable_if<std::is_same<U, int>::value, int>::type
g() {
std::cout << "int\n";
return int();
}
public:
static T f() { return g<T>(); }
};
int main(void)
{
A<char>::f();
A<int>::f();
// error: no matching function for call to ‘A<double>::g()’
// A<double>::f();
return 0;
}
答案 3 :(得分:3)
标签调度通常是干净的方法。
在基本方法中,使用traits类来确定要调用的方法的子版本。这会生成一个描述决策结果的类型(称为标签)。
然后完美地转发传递标签类型实例的实现子版本。重载决策启动,只有你想要的实现被实例化和调用。
基于参数类型的重载分辨率是处理调度的一种不那么疯狂的方式,因为enable_if
是脆弱的,在使用时很复杂,如果你有3+重载就变得非常复杂,并且有奇怪的可以通过精彩的编译错误让您惊讶的角落案例。
答案 4 :(得分:1)
也许我错了,但选择Manu343726提供的最好的anwser有一个错误并且不会编译。两个运算符重载都具有相同的签名。考虑问题std::enable_if : parameter vs template parameter
中最好的anwserP.S。我会发表评论,但声誉不够,抱歉