我一直在寻找解决以下问题的设计。 用几句话来形容。
我们有四种类型A1,A2,B和C.
我们想写一个函数fn,它将参数作为类型P。 使用特征,P在A1 / A2 / B / C内解析为PA1 / PA2 / PB / PC。
对于PA1和PA2,fn的实现是相同的,但确实如此 不同的PB和PC,它们之间以及它们之间的不同 PA1 / PA2。
#include <cassert>
struct PA1 {};
struct PA2 {};
struct PB {};
struct PC {};
struct A1 { typedef PA1 P; };
struct A2 { typedef PA2 P; };
struct B { typedef PB P; };
struct C { typedef PC P; };
template<typename T> char fn(typename T::P)
{
return 'a';
}
char fn(B::P) { return 'b'; }
char fn(C::P) { return 'c'; }
int main()
{
PA1 pa1;
PA2 pa2;
PB pb;
PC pc;
assert( fn<A1>(pa1) == 'a' );
assert( fn<A2>(pa2) == 'a' );
assert( fn(pb) == 'b' );
assert( fn(pc) == 'c' );
}
上面代码的优点是fn的实现 PA1和PA2不重复。
但这是障碍。函数调用不是对称的。它是
fn<A1>(pa1)
和
fn<A2>(pa2)
表示A1 / A2 / PA1 / PA2,但只有fn(pb)
和
B / C / PB / PC fn(pc)
。
这排除了在另一个(未示出)类中使用fn(..) 模板。
通常这不是问题。可以推导出模板参数 用于参数化功能。这在这里不起作用。我们要问 编译器在P解析的A1 / A2 / B / C中找到类型 到PA1 / PA2 / PB / PC之一。
你会做什么?
答案 0 :(得分:1)
我认为你在这里混淆了两件事:
的实施
fn
和PA1
PA2
相同
不会阻止PA1
和PA2
成为具有不同typedef
的不同类型。
我们可以按原样使用:
template <typename PA>
char fn(PA pa) {
typedef typename PA::type Type; // A1 or A2
return 'a';
}
char fn(PB);
char fn(PC);
如果您无法实际修改P
本身,则可以随时引入特征类。
template <typename T>
struct PTraits;
template <>
struct PTraits<PA1> { typedef A1 type; };
template <>
struct PTraits<PA2> { typedef A2 type; };
// ...
我会注意到Herb Sutter建议不要使用函数特化并且更喜欢重载,因为重载之间的交互(特别是涉及模板)是专门化非常棘手......并且很难理解哪个函数会被调用来自一组给定的参数。
答案 1 :(得分:0)
你真的不能让编译器逆转;如果两个结构P
为typedef
int
,会发生什么?
如果执行具有T
到T::P
的一对一映射,那么您需要告诉编译器如何从T::P
返回明确地T
。
答案 2 :(得分:0)
为了保持对称,您可以使用模板专业化:
template<> char fn<B>(B::P) { return 'b'; }
template<> char fn<C>(C::P) { return 'c'; }
然后你的电话看起来都像:
assert( fn<B>(pb) == 'b' );
assert( fn<C>(pc) == 'c' );
有趣的是,即使使用明确的模板实例化,例如:
template char fn<A1>(A1::P);
template char fn<A2>(A2::P);
我们没有得到推断类型。