右值引用的一些研究

以下的内容是最新的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见以后随笔

posted on 2011-08-31 18:34  Observer  阅读(339)  评论(0编辑  收藏  举报

导航