以下的内容是最新的c++0x标准当中的,其中vs2010是符合这个标准的TypeName&&表示右值引用类型:
为了判断一个表达式是左值还是右值,第一个想法是写函数来判断,函数不能模板特化,因此通过重载来确定是不错的方法:
第一个函数接受非const左值:
template<class T>
void GetValType(T&)
{
cout << "Not a Right Value, a T" << endl;
}
第二个函数接受const左值:
template<class T>
void GetValType(const T&)
{
cout << "Not a Right Value, a const T" << endl;
}
第三个函数接受非const右值:
template<class T>
void GetValType(T&&)
{
cout << "A Right Value" << endl;
}
第四个函数接受const右值:
template<class T>
void GetValType(const T&&)
{
cout << "A Const Right Value" << endl;
}
为了获得一个常数右值,定义一个函数:
const int RetAnIntLVal(){return 0;}
那么对于以下的语句:
GetValType(1 + 1);
const int ia = 0;
GetValType(ia);
GetValType(RetAnIntLVal());
输出的结果是:
A Nonconst Right Value
A Const Left Value
A Const Right Value
但是对于以下的语句
int ib = 0;
GetValType(ib);
却得到了编译错误:
error C2668: 'GetValType' : ambiguous call to overloaded function
更详细的编译错误为:
could be 'void GetValType<int&>(T)'
with
[
T=int &
]
'void GetValType<int>(T &)'
with
[
T=int
]
while trying to match the argument list '(int)'
就是说一个int的值可以匹配到(第三个函数)
void GetValType(int& &&)
也可以匹配到(第一个函数)
void GetValType(int&)
其实当初想的是只匹配上述两个的后者,但是前者为什么会也能够匹配到呢,而且同样是精确匹配(重载无法选择更好的,后者又明显是精确匹配),答案就出在了下表中,
the reference collapsing rules for template argument type deduction:
Expanded type |
Collapsed type |
T& & |
T& |
T& && |
T& |
T&& & |
T& |
T&& && |
T&& |
就是说左边的类型会坍塌成为右边的类型,恰好第二个坍塌就是前一个函数的情况,那么这个问题如何解决呢?
考虑如下一个外层函数:
template<class T>
void OuterGet(T&& temp)
{//forward是完美转发,将转发temp的真实类型给另一个函数作为实参
GetValType<T&&>(forward<T>(temp));
}
但是这样的函数仍然是错误的,给出的编译错误令人震惊:
当OuterGet函数以T=int &绑定时,那么对于GetValType来说T = int & &&即int &这样按理说模板函数1就会实例化成为GetValType(int&),这是最贴切的,但是编译器给出的答案是它把函数2和函数4实例化成GetValType(int)这样就ambiguous了,然后报错.我具体也不知道是怎么回事,苦思一日之后编了一个理由来解释:函数模板类型在显示指定的时候不支持类型坍塌(Type Collapse),而是会出现一些莫名奇妙的结果 注1.
既然函数类型不支持,那么模板类型定然是支持的,这样也符合表给出的意思:仅仅支持在模板实参推导中,函数实参推导就不支持了.
于是就有了以下的实现代码:
#include <iostream> using namespace std; template<class T> struct Help; template<class T> struct Help<T&> { static void GetValType(T&) { cout << "A Nonconst Left Value" << endl; } }; template<class T> struct Help<const T&> { static void GetValType(const T&) { cout << "A Const Left Value" << endl; } }; template<class T> struct Help<T&&> { static void GetValType(T&&) { cout << "A Nonconst Right Value" << endl; } }; template<class T> struct Help<const T&&> { static void GetValType(const T&&) { cout << "A Const Right Value" << endl; } }; template<class T> void Deliver(T&& temp) { Help<T&&>::GetValType(forward<T>(temp)); } int main() { int ia = 0; Deliver(ia); const int ib = 0; Deliver(ib); Deliver(0); Deliver(RetAnIntLVal()); }
以上代码就可以完成正确的任务.
注1见以后随笔