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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33240993/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:14:08  来源:igfitidea点击:

C++ Difference between std::ref(T) and T&?

c++referenceref

提问by CppNITR

I have some questions regarding this program:

我有一些关于这个程序的问题:

#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;
}

The output is:

输出是:

false

I want to know, if std::refdoesn't return the reference of an object, then what does it do? Basically, what is the difference between:

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

T x;
auto r = ref(x);

and

T x;
T &y = x;

Also, I want to know why does this difference exist? Why do we need std::refor std::reference_wrapperwhen we have references (i.e. T&)?

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

采纳答案by Ankit Acharya

Well refconstructs an object of the appropriate reference_wrappertype to hold a reference to an object. Which means when you apply:

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

auto r = ref(x);

This returns a reference_wrapperand not a direct reference to x(ie T&). This reference_wrapper(ie r) instead holds T&.

这将返回 areference_wrapper而不是对x(ie T&)的直接引用。这reference_wrapper(即r)反而成立T&

A reference_wrapperis very useful when you want to emulate a referenceof an object which can be copied (it is both copy-constructibleand copy-assignable).

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

In C++, once you create a reference (say y) to an object (say x), then yand xshare the same base address. Furthermore, ycannot refer to any other object. Also you cannot create an array of referencesie code like this will throw an error:

在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;
}

However this is legal:

但是这是合法的:

#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
*/

Talking about your problem with cout << is_same<T&,decltype(r)>::value;, the solution is:

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

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

Let me show you a program:

我给你看一个程序:

#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
*/

See here we get to know three things:

看到这里我们就知道三件事:

  1. A reference_wrapperobject (here r) can be used to create an array of referenceswhich was not possible with T&.

  2. ractually acts like a real reference (see how r.get()=70changed the value of y).

  3. ris not same as T&but r.get()is. This means that rholds T&ie as its name suggests is a wrapper around a referenceT&.

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

  2. r实际上就像一个真正的引用(看看r.get()=70的值是如何改变的y)。

  3. rT&但不一样r.get()。这意味着rhold T&ie 顾名思义是一个引用包装器T&

I hope this answer is more than enough to explain your doubts.

我希望这个答案足以解释你的疑惑。

回答by Quentin

std::reference_wrapperis recognized by standard facilities to be able to pass objects by reference in pass-by-value contexts.

std::reference_wrapper被标准设施识别为能够在传值上下文中通过引用传递对象。

For example, std::bindcan take in the std::ref()to something, transmit it by value, and unpacks it back into a reference later on.

例如,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();
}

This snippet outputs :

此代码段输出:

10
20

The value of ihas been stored (taken by value) into f1at the point it was initialized, but f2has kept an std::reference_wrapperby value, and thus behaves like it took in an int&.

的值在初始化时i已被存储(按值获取)f1,但f2保留了一个std::reference_wrapperby 值,因此它的行为就像它在一个int&.

回答by Serge Ballesta

A reference (T&or T&&) is a special element in C++ language. It allows to manipulate an object by referenceand has special use cases in the language. For example, you cannot create a standard container to hold references: vector<T&>is ill formed and generates a compilation error.

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

A std::reference_wrapperon the other hand is a C++ object able to hold a reference. As such, you can use it in standard containers.

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

std::refis a standard function that returns a std::reference_wrapperon its argument. In the same idea, std::crefreturns std::reference_wrapperto a const reference.

std::ref是一个标准函数,std::reference_wrapper它在其参数上返回 a 。同理,std::cref返回std::reference_wrapper一个常量引用。

One interesting property of a std::reference_wrapper, is that it has an operator T& () const noexcept;. That means that even if it is a true object, it can be automatically converted to the reference that it is holding. So:

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

  • as it is a copy assignable object, it can be used in containers or in other cases where references are not allowed
  • thanks to its operator T& () const noexcept;, it can be used anywhere you could use a reference, because it will be automatically converted to it.
  • 因为它是一个可复制赋值的对象,所以它可以用于容器或其他不允许引用的情况
  • 由于它的operator T& () const noexcept;,它可以在任何可以使用引用的地方使用,因为它会自动转换为它。