C++的模板(十二):forward模板
标准库的std::forward模板有一个类型T参数。在实际使用时,T取什么值应该不存在疑惑:
class A {
public:A() {}A(int n):n(n) {}template <class T> A(T &&a);void print(A &&a) { printf("A&&\n"); }void print(A &a) { printf("A&\n"); }void print(const A &a) { printf("const A&\n"); }int n;
};template <class T> A::A(T &&a)
{printf("template %s(&&a): a ",__FUNCTION__);print(std::forward<T>(a));n= a.n+1;
}
这个例子中,复制构造函数写成了模板。呵呵,广义复制构造函数。这不是太可靠。不过这个例子不会乱传不可使用的类型T,这样写已经足够。在复制构造函数模板中,向重载的print()成员函数转发了参数a。模板并不清楚传给它的类型是哪种重载类型,因此转发给print(),让它打印出来。这里,forward模板携带的参数类型T,照抄模板的传入类型。
A f(int n)
{A a;a.n=n;printf("A f(%d) return\n", n);return a;
}A g(int n)
{printf("A g(%d) return\n", n);return A(n);
}int main()
{A a; a.n=900;printf("f(100): \n");A b=f(100);printf("%d\n", b.n);printf("g(100): \n");A b1=g(100);printf("%d\n", b1.n);printf("*******\n");const A *pa= &a;A b2= *pa;printf("%d\n", b2.n);printf("c:");A c(a);printf("c.n=%d\n", c.n);
}
打印出来是这样的信息:
f(100):
A f(100) return
template A(&&a): a A&
template A(&&a): a A&&
102
g(100):
A g(100) return
template A(&&a): a A&&
template A(&&a): a A&&
102
*******
b2: 900
c:template A(&&a): a A&
c.n=901
f()和g()函数的"赋值"语句(赋值格式的初始化语句)各导致了2次复制构造函数调用。一次return语句向返回值复制,一次返回值向接收的变量复制。第2次都是移动构造,第一次在g()函数也是移动构造。
但是, b2并没有调用这个“广义复制构造函数”。如果没有显式定义复制构造函数,C++会自动生成一个。这里调用的正是C++自动生成那个。模板复制构造函数不影响C++自动生成构造函数。所以用“广义复制构造函数抓取const 引用”的设想落空了。为了证实这一点,可以塞给class A一个base类,在base里面打印复制构造函数。如果调用的是C++自动生成那个版本,会自动调用基类的对应的复制构造函数。也就是base(const base &b);
class base {
public:base(){}base(const base &b) { printf("copy constructor(const&)\n");}base(base &b) { printf("copy constructor(&)\n");}base(base &&b) { printf("copy constructor(&&)\n");}
};
class A :public base{
public:A() {}A(int n):n(n) {}template <class T> A(T &&a);void print(A &&a) { printf("A&&\n"); }void print(A &a) { printf("A&\n"); }void print(const A &a) { printf("const A&\n"); }int n;
};
别的代码不变。运行结果提示,b2确实调用了C++自动生成的那个复制构造函数。所以复制构造函数模板不是很圆满。
例子中的print()打印了类型信息。这个信息很有用。虽然,print没有返回值,但可以让它有。
int print(A &&a) { printf("A&&\n"); return 1; }int print(A &a) { printf("A&\n"); return 2;}int print(const A &a) { printf("const A&\n"); return 3;}
这样模板用forward转发一下,从返回值就可以知道自己参数是什么类型了。
template <class T> A::A(T &&a)
{int ret;printf("template %s(&&a): a ",__FUNCTION__);ret =print(std::forward<T>(a));switch (ret) {//......}n= a.n+1;
}
这样就可以对不同的类型,做不同的处理。经过forward,模板是可以知道自己的传入类型。
意识到这种识别对任意类型的通用性,最终把这也写成了模板:
template <class T>
struct eval_forward_t {typedef typename std::remove_reference<T>::type noref;typedef typename std::remove_const<noref>::type type;static int eval(type &&a) {return 1;}static int eval(type &a) {return 2;}static int eval(const type &a) {return 3;}
};template <class T> A::A(T &&a)
{printf("template %s(&&a): a ",__FUNCTION__);print(std::forward<T>(a));printf("eval_forward: %d\n",eval_forward_t<T>::eval(std::forward<T>(a)));n= a.n+1;
}