C ++ std :: ref(T)和T&?之间的区别


92

我对此程序有一些疑问:

#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
template <typename T> void foo ( T x )
{
    auto r=ref(x);
    cout<<boolalpha;
    cout<<is_same<T&,decltype(r)>::value;
}
int main()
{
    int x=5;
    foo (x);
    return 0;
}

输出为:

false

我想知道,如果std::ref不返回对象的引用,那它有什么作用?基本上,两者之间有什么区别?

T x;
auto r = ref(x);

T x;
T &y = x;

另外,我想知道为什么存在这种差异?为什么需要我们std::refstd::reference_wrapper有参考文献(例如T&)?



提示:如果x = y; 在两种情况下都进行会怎样?
juanchopanza

2
除了我的重复标记(最新评论)之外:例如,请参见stackoverflow.com/questions/31270810/…stackoverflow.com/questions/26766939/…–
anderas

2
@anderas不是有用,主要是区别
CppNITR

@CppNITR然后在评论前几秒钟看到我链接的问题。特别是第二个有用。
anderas

Answers:


91

ref构造一个适当reference_wrapper类型的对象,以保存对对象的引用。这意味着您申请时:

auto r = ref(x);

这将返回areference_wrapper而不是直接引用x(例如T&)。这个reference_wrapper(即r)成立T&

reference_wrapper,当你想模仿是非常有用的reference,可复制的对象(它既是拷贝构造拷贝分配)。

在C ++中,一旦创建参考(比方说y)一个对象(比如x),然后yx共享相同的基地址。此外,y不能引用任何其他对象。另外,您不能创建引用数组,即像这样的代码将引发错误:

#include <iostream>
using namespace std;

int main()
{
    int x=5, y=7, z=8;
    int& arr[] {x,y,z};    // error: declaration of 'arr' as array of references
    return 0;
}

但这是合法的:

#include <iostream>
#include <functional>  // for reference_wrapper
using namespace std;

int main()
{
    int x=5, y=7, z=8;
    reference_wrapper<int> arr[] {x,y,z};
    for (auto a: arr)
        cout << a << " ";
    return 0;
}
/* OUTPUT:
5 7 8
*/

谈到您的问题cout << is_same<T&,decltype(r)>::value;,解决方案是:

cout << is_same<T&,decltype(r.get())>::value;  // will yield true

让我给你看一个程序:

#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;

int main()
{
    cout << boolalpha;
    int x=5, y=7;
    reference_wrapper<int> r=x;   // or auto r = ref(x);
    cout << is_same<int&, decltype(r.get())>::value << "\n";
    cout << (&x==&r.get()) << "\n";
    r=y;
    cout << (&y==&r.get()) << "\n";
    r.get()=70;
    cout << y;
    return 0;
}
/* Ouput:
true
true
true
70
*/

看到这里,我们了解三件事:

  1. reference_wrapper对象(这里r)可被用于创建引用的数组这是不可能的T&

  2. r实际上就像一个真实的参考一样(请参阅如何r.get()=70更改的值y)。

  3. r不一样的T&,但是r.get()是。这意味着,r拥有T&即顾名思义是围绕基准包装 T&

我希望这个答案足以解释您的疑问。


3
1:否,reference_wrapper可以重新分配a,但是它不能“保留对多个对象的引用”。2/3:关于何处.get()适当的公平点-但是r 可以使用后缀,就像可以明确调用转换的T&情况一样-因此,在很多情况下都无需调用,包括代码中的多次调用(很难阅读)由于缺少空间)。roperator.get()
underscore_d 2015年

reference_wrapper如果不确定,@ underscore_d可以保存引用数组,然后可以自己尝试。另外.get(),当你想改变对象的值被用来reference_wrapper为holding`即r=70是非法的,所以你必须使用r.get()=70。尝试一下!
Ankit Acharya 2015年

1
并不是的。首先,让我们使用准确的措词。您正在显示一个reference_wrapper,它持有对数组的引用-而不是本身包含“对多个对象的引用”的reference_wrapper。包装器仅保存一个引用。其次,我可以很好地获得对数组的本地引用-您确定您不只是忘记了周围的(括号)int a[4]{1, 2, 3, 4}; int (&b)[4] = a;吗?reference_wrapper在这里并不特殊,因为nativeT& 可以工作。
underscore_d

1
@AnkitAcharya是:-),但确切地说,除了有效的结果,它本身仅指一个对象。无论如何,您当然是对的,与普通参考不同,它们wrapper可以放入容器中。这很方便,但是我认为人们会误解为比实际情况更先进。如果我想要一系列“ refs”,我通常用跳过中间人vector<Item *>,这wrapper归结为...,并希望反指针纯粹主义者找不到我。令人信服的用例是不同的,而且更加复杂。
underscore_d 2015年

1
“当您要模拟可复制对象的引用时,reference_wrapper非常有用。” 但这是否违反了参考的目的?您指的是实际情况。那就是参考。可以复制的引用似乎毫无意义,因为如果您要复制它,那么首先就不需要引用,您应该只复制原始对象。解决似乎根本不需要存在的问题似乎是不必要的复杂性层。
斯图

53

std::reference_wrapper 标准工具将其识别为能够在按值传递上下文中通过引用传递对象。

例如,std::bind可以接受std::ref()某物,按值传输它,然后将其拆包回到参考中。

void print(int i) {
    std::cout << i << '\n';
}

int main() {
    int i = 10;

    auto f1 = std::bind(print, i);
    auto f2 = std::bind(print, std::ref(i));

    i = 20;

    f1();
    f2();
}

此代码段输出:

10
20

的值i已在f1初始化时存储(按值)到其中,但f2保留了一个std::reference_wrapper按值,因此其行为类似于将其带入int&


2
@CppNITR肯定!给我一点时间来组装一个小演示:)
Quentin

1
T&&ref(T)之间的区别如何
CppNITR

3
@CppNITRstd::ref(T)返回一个std::reference_wrapper。它只不过是一个包装的指针,而是被库识别为“嘿,我应该作为参考!一旦将我传递出去,请把我重新变成一个”。
2015年

38

引用(T&T&&)是C ++语言中的特殊元素。它允许通过引用来操作对象并具有该语言的特殊用例。例如,您不能创建一个标准容器来保存引用:格式vector<T&>不正确并生成编译错误。

std::reference_wrapper另一方面,A是一个能够保存引用的C ++对象。因此,您可以在标准容器中使用它。

std::ref是一个标准函数,std::reference_wrapper其参数返回a 。同样,std::cref返回std::reference_wrapperconst引用。

a的一个有趣特性std::reference_wrapper是它具有一个operator T& () const noexcept;。这意味着,即使它是一个真实的对象,也可以将其自动转换为它所持有的引用。所以:

  • 由于它是可分配的副本对象,因此可以在容器中使用或在不允许引用的其他情况下使用
  • 多亏了它的支持operator T& () const noexcept;,它可以在任何可以使用引用的地方使用,因为它将被自动转换为引用。

1
之所以投票,主要是因为没有提及operator T& ()其他2个答案。
metablaster
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.