我有一个模板化的类,在里面我有一个模板化的函数(不同的模板参数),我在编译器调用正确的函数时遇到了问题。
示例:
template< class Parm1, class Parm2, class Parm3 >
class Class {
public:
void Func( Parm1 arg1, Parm2 arg2 ) {
Call<Parm3>( arg1, arg2 );
}
protected:
template< class Type >
void Call( Parm1 arg1, Parm2 arg2 ) {
}
template<>
void Call<void>( Parm1 arg1, Parm2 arg2 ) {
}
};
因此,如果Parm3的类型为'void',我希望调用第二个Call。否则第一个。 VS它工作正常,但GCC呕吐了它。它总是称为第一个。现在这是一个专门针对非专业课程的问题,还是与我专注于'void'的事实有关
任何帮助都会很棒。感谢。
答案 0 :(得分:7)
是的,明确地专门化一个函数而不完全专门化所有外部模板是不可能的(显式函数专门化是一个真正的函数 - 它周围不能有任何“可变部分”仍然由模板参数化)
一种简单的方法是使用type2type模板和重载:
template<typename T> struct t2t { typedef T type; };
void Func( Parm1 arg1, Parm2, arg2 ) { Call<Parm3>(arg1, arg2, t2t<Parm3>()); }
template< class Type, class V > void Call( Parm1 arg1, Parm2 arg2, t2t<V>) { }
template< class Type > void Call( Parm1 arg1, Parm2 arg2, t2t<void>) { }
现在,如果你用Call
调用它会调用第二个t2t<void>
重载,否则会调用第一个{1}}重载,因为第一个不太特殊。
也可以使用enable_if
:
void Func( Parm1 arg1, Parm2, arg2 ) { Call<Parm3>(arg1, arg2); }
template< class Type > typename disable_if< is_same<Type, void> >::type
Call( Parm1 arg1, Parm2 arg2) { }
template< class Type > typename enable_if< is_same<Type, void> >::type
Call( Parm1 arg1, Parm2 arg2) { }
现在,如果Type
无效,则采取第二个,如果Type
再次为其他,则采取第一个。但使用不同的技术。这个名为SFINAE
。另一种方法,但是再添加一个参数是为了证明SFINAE的工作方式:
void Func( Parm1 arg1, Parm2, arg2 ) { Call<Parm3>(arg1, arg2); }
template< class Type >
void Call( Parm1 arg1, Parm2 arg2, char(*)[!is_same<Type, void>::value] = 0) { }
template< class Type >
void Call( Parm1 arg1, Parm2 arg2, char(*)[ is_same<Type, void>::value] = 0) { }
如果模板参数的替换产生无效的类型或构造,则会发生 SFINAE
。下面,我们尝试分别创建一个指向大小为0或1的数组的指针。大小为0的数组无效,并且会导致SFINAE失败 - 如果它是一个函数,相应的模板特化将不会被视为调用候选者。
在上面的enable_if
案例中,它的工作方式不同。如果enable_if
被赋予从false_type
派生的内容,则会使其::type
typedef不存在。 is_same
在案例类型不同的情况下从false_type
派生出来。然后我们会尝试访问一个不存在的名称 - 这是一个无效的构造,因此也会出现SFINAE故障。