正如 Alexender C. 在评论中提到的那样,您确定编译错误不会更合适吗?
template <typename T> struct GateKeeper;
// Func we want to call
template <typename S, typename T> void foo (T t);
// Warpper that checks the type and forwards the call
template <typename T> inline void foo (T t)
{
//
// This call will fail for unless a specialization of
// GateKeeper for `T` is defined with a member TYPE
foo< typename GateKeeper<T>::TYPE, T > ( t );
}
//
// This declaration "allows" the type int.
template <> struct GateKeeper<int> { typedef int TYPE; };
void bar ()
{
foo (0); // Compiles
foo (0.0); // Causes error in wrapping foo
}
部分特化可用于允许对给定模板进行任何特化:
// Some template type
template <typename T>
class TmplType
{
};
// This allows for specializations of TmplType
template <typename T> struct GateKeeper< TmplType<T> > { typedef int TYPE; };
void bar ()
{
TmplType<char> tt;
foo (tt); // Compiles
}
对于您希望支持的每种类型,您都添加一个新的专业化。
更新:发生了什么:
注意:我已经更改了原始版本的模板参数名称,以使事情更加清晰。
当编译器看到foo(x)
对它可以正确专门化的唯一函数的调用时foo<T>(T)
. 这是因为它无法推导出foo<S,T>(T)
.
的主体foo<T>(T)
将调用转发给真正的函数:
foo< typename GateKeeper<T>::TYPE, T > ( t );
(旁白:有关何时使用typename的说明,请参见此处)
这是一次做两件事。
第一个是提供调用另一个函数所需的 2 个模板参数(S 和 T)。
第二个是使用一个成员GateKeeper<T>
作为这个其他类型。类型GateKeeper<T>
必须是完整的并且具有该成员。正是这个检查允许我们指定我们想要允许哪些类型,我们不允许哪些类型:
template <typename T> struct GateKeeper; // Incomplete
template <> struct GateKeeper<int> { typedef int TYPE; }; // Complete
由于我们只提供了 forGateKeeper<int>
而不是 for的定义,因此对GateKeeper<double>
的调用可以foo(0)
正常工作,并且会foo(0.0)
因编译错误而失败。
为了允许double
,我们只需要为它添加一个明确的特化:
template <> struct GateKeeper<double> { typedef int TYPE; }; // double now works.