标准库的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;
}