右值引用的一些研究

以下的内容是最新的c++0x标准当中的,其中vs2010是符合这个标准的TypeName&&表示右值引用类型:

为了判断一个表达式是左值还是右值,第一个想法是写函数来判断,函数不能模板特化,因此通过重载来确定是不错的方法:

第一个函数接受非const左值:

template

void GetValType(T&)

{

cout << "Not a Right Value, a T" << endl;

}

第二个函数接受const左值:

template

void GetValType(const T&)

{

cout << "Not a Right Value, a const T" << endl;

}

第三个函数接受非const右值:

template

void GetValType(T&&)

{

cout << "A Right Value" << endl;

}

第四个函数接受const右值:

template

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(T)'

with

[

T=int &

]

'void GetValType(T &)'

with

[

T=int

]

while trying to match the argument list '(int)'

就是说一个int的值可以匹配到(第三个函数)

void GetValType(int& &&)

也可以匹配到(第一个函数)

void GetValType(int&)

其实当初想的是只匹配上述两个的后者,但是前者为什么会也能够匹配到呢,而且同样是精确匹配(重载无法选择更好的,后者又明显是精确匹配),答案就出在了下表中,

the reference collapsing rules fortemplate argument type deduction:

Expanded type Collapsed type
T& & T&
T& && T&
T&& & T&
T&& && T&&

上表出处

就是说左边的类型会坍塌成为右边的类型,恰好第二个坍塌就是前一个函数的情况,那么这个问题如何解决呢?

考虑如下一个外层函数:

template

void OuterGet(T&& temp)

{//forward是完美转发,将转发temp的真实类型给另一个函数作为实参

GetValType(forward(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见以后随笔
原文链接: https://www.cnblogs.com/observer/archive/2011/08/31/2161164.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/31523

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月8日 上午8:47
下一篇 2023年2月8日 上午8:48

相关推荐