std::is_same具有左值和右值引用的结果

std::is_same result with lvalue and rvalue reference

本文关键字:结果 引用 is same std      更新时间:2023-10-16

我在玩std::is_same实用函数与右值和左值引用的组合时,遇到了一个奇怪的行为。

考虑一下这个函数模板,它检查变量t的类型。

我正在使用VS 2013:

struct Test {};
template < class T> void h(T && t)
{ 
    cout << " Is type &&: " << std::is_same<decltype(t), T &&>::value << endl;
    cout << " Is type &: " << std::is_same<decltype(t), T &>::value << endl;
}

我观察到以下输出:

h(Test()); // is type && : 1  is type & : 0

目前这是正常的,因为Test()是一个临时对象,参数h中的通用引用解析为r值引用(&&=&)

但考虑一下:

Test myTest;
h(myTest); // is type && : 1  is type & : 1 !!!

相同的结果,如果我写:

Test &myTest = Test():
h(myTest); // is type && : 1  is type & : 1 !!!

与相同

Test &&myTest = Test():
h(myTest); // is type && : 1  is type & : 1 !!!

我是不是错过了什么?在我看来,这完全是一团糟:)VS 2013中是否不完全支持rvalue-reference/deltype功能?

谢谢你的帮助

罗曼

Romain,这很容易。让我们考虑以下情况:

Test myTest;
h(myTest); // is type && : 1  is type & : 1 !!!

在h内部,T是Test&,并且T是类型Test& &&,即Test&。进行测试时,std::is_same<decltype(t), T &&>为true,因为Test& &&Test&,而t类型是Test&

std::is_same<decltype(t), T &>也是真的,因为t类型是Test&,而T&Test&&,即Test&

仔细应用引用折叠规则在这里会有所帮助。

关于为什么内部的h()T是Test&类型。原因是它是唯一一个与实际参数匹配的T类型。T不能简单地是Test&将不会绑定(匹配)类型为Test的左值(因为这是参数类型)。但是,由于引用折叠规则的原因,Test& &&会。

我将从这里开始:

template < class T> void h(T && t)
{ 
  std::cout << " Is type &&: " << std::is_same<decltype(t), T &&>::value << 'n';
  std::cout << " Is type &: " << std::is_same<decltype(t), T &>::value << 'n';
}

在第一个测试中,您询问decltype(t)是否与T&&相同。

对于变量,decltype(variable)是该变量的声明类型。看看t是如何声明的——它就是T&&

毫不奇怪,decltype(t)T&&相同,因为T&& t被声明为T&&

无论T是什么,这都是真的。因此,无论你通过哪种类型的T,你都会在这条线上获得true。

抓住这个。理解这一点。您无法为T匹配的任何类型都可能使第一行永远无法打印1。如果您对第一行感到困惑,请回到它正在执行std::is_same< T&&, T&& >::value的事实。无论T&&的规则是什么,双方都是一样的。

但等等,你说,T&&不是一个右值参考吗?嗯,通常。如果类型类型T是引用类型,则应用引用折叠规则。当T=X&变为X&而不是X&&时的T&&。左值引用胜过右值引用。

通过特殊的推导规则,这就是转发引用(又称通用引用)的动力。

当您将类型为X的左值传递给h时,它将推导出T=X&

当您将类型为X的右值传递给h时,它将推导出T=X

在第一种情况下,T&&变成X&,在第二种情况下变成X&&

在你的具体例子中,Test()Test类型的右值,这意味着h(Test())推导出T=Test,我们得到T&&=Test&&,而T&=Test&。代码打印1然后打印0,因为T&&(又名decltype(t),又名Test&&)与T&&相同,但与T&不同。

在第二种具体情况中,h(myTest)myTest作为左值(即使它被声明为Test&&,它也是一个左值,因为它有一个名称)。则TTest&T&&Test&T&Test&。函数打印出1然后1,因为T&&(又名decltype(t),又名Test&)与T&&T&相同。

要解决您的问题,请执行以下操作:

template < class T> void h(T && t)
{ 
  using rawT = std::remove_reference_t<T>;
  std::cout << " Is type &&: " << std::is_same<decltype(t), rawT &&>::value << 'n';
  std::cout << " Is type &: " << std::is_same<decltype(t), rawT &>::value << 'n';
}

并且你得到了你期望的输出。