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

Cpp*_*ITR 76 c++ reference ref

我对这个程序有一些疑问:

#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;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

false
Run Code Online (Sandbox Code Playgroud)

我想知道,如果std::ref没有返回对象的引用,那么它会做什么?基本上,有什么区别:

T x;
auto r = ref(x);
Run Code Online (Sandbox Code Playgroud)

T x;
T &y = x;
Run Code Online (Sandbox Code Playgroud)

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

Ank*_*rya 72

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

auto r = ref(x);
Run Code Online (Sandbox Code Playgroud)

这将返回a reference_wrapper而不是对x(ie 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;
}
Run Code Online (Sandbox Code Playgroud)

但这是合法的:

#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
*/
Run Code Online (Sandbox Code Playgroud)

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

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

让我告诉你一个程序:

#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
*/
Run Code Online (Sandbox Code Playgroud)

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

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

  2. r实际上就像一个真正的参考(看看如何r.get()=70改变其价值y).

  3. r不一样的T&,但是r.get()是.这意味着r保持T&即顾名思义是引用包装T&.

我希望这个答案足以解释你的疑虑.

  • 1:不,`reference_wrapper`可以是_reassigned_,但是不能“保留对多个对象的引用”。2/3:关于.get()适用的公平点-在可以明确调用`r`的转换`operator`的情况下,_but_不带后缀的`r` _can_可以与`T&`相同使用。在许多情况下,都需要调用.get(),包括代码中的多个(由于缺少空格而难以阅读)。 (2认同)
  • 并不真地。首先,让我们使用准确的措辞。您正在展示一个 reference_wrapper 持有 _a 对数组的引用_ - 而不是一个 reference_wrapper 本身_包含“对多个对象的引用”_。包装器只保存一个引用。其次,我可以很好地获得对数组的本机引用 - 你确定你没有忘记 `int a[4]{1, 2, 3, 4}; 周围的(括号)吗?int (&amp;b)[4] = a;`? reference_wrapper 在这里并不特别,因为原生`T&amp;` _确实可以工作。 (2认同)

Que*_*tin 46

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();
}
Run Code Online (Sandbox Code Playgroud)

此代码段输出:

10
20
Run Code Online (Sandbox Code Playgroud)

i已被存储(由值取)到f1它初始化的点,但是f2保持了一个std::reference_wrapperby值,因此表现得像一个int&.

  • @CppNITR`std :: ref(T)`返回一个`std :: reference_wrapper`.它只不过是一个包裹的指针,但是被图书馆认为是"嘿,我应该是一个参考!一旦你完成了我,请把我变成​​一个". (3认同)
  • @CppNITR肯定!给我一点时间来组装一个小演示:) (2认同)

Ser*_*sta 31

引用(T&T&&)是C++语言中的特殊元素.它允许通过引用操作对象,并在语言中具有特殊用例.例如,您无法创建标准容器来保存引用:vector<T&>生成错误并生成编译错误.

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

std::ref是一个返回std::reference_wrapper其参数的标准函数.在同一个想法中,std::cref返回std::reference_wrapper一个const引用.

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

  • 因为它是一个可复制的对象,所以它可以用在容器中或不允许引用的其他情况下
  • 由于它operator T& () const noexcept;,它可以在任何可以使用引用的地方使用,因为它会自动转换为它.

  • 投票主要是因为提到了“operator T&amp; ()”,而其他 2 个答案没有提及。 (2认同)