与常规引用相比,std::ref 和 std::reference_wrapper 有何优势?

问题描述 投票:0回答:4

我对此计划有一些疑问:

#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::ref
)时,为什么我们需要
std::reference_wrapper
T&

c++ reference reference-wrapper
4个回答
108
投票

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

auto r = ref(x);

这将返回一个

reference_wrapper
,而不是对
x
的直接引用(即
T&
)。这个
reference_wrapper
(即
r
)改为持有
T&

当您想要模拟可复制对象的

reference_wrapper

 时,
A
reference
非常有用(它既是 copy-constructible 又是 copy-assignable)。

在 C++ 中,一旦创建对对象(例如

y
)的引用(例如
x
),那么
y
x
共享相同的 基地址。此外,
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&

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


60
投票

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&
中那样.


43
投票

引用(

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

另一方面,
A

std::reference_wrapper

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


std::ref

是一个标准函数,在其参数上返回

std::reference_wrapper
。同样的想法,
std::cref
std::reference_wrapper
返回给const引用。

std::reference_wrapper

的一个有趣的属性是它有一个

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

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

0
投票

std::bind 复制提供的参数,除非它是由 std::ref()/std::cref() 传递的。

void f(int r1, int& r2, int w1, int& w2) { std::cout << r1 << r2 << w1 << w2; // 5 5 10 10 r1 = 9, r2 = 9, w1 = 9, w2 = 9; } int main() { int w1 = 5, w2 = 5, n1 = 5, n2 = 5; int& r1 = n1; int& r2 = n2; std::function<void()> bound_f = std::bind(f, r1, r2, std::ref(w1), std::ref(w2)); r1 = 10, r2 = 10, w1 = 10, w2 = 10; bound_f(); // 5 5 10 10 std::cout << r1 << r2 << w1 << w2; // 10 10 10 9 }

© www.soinside.com 2019 - 2024. All rights reserved.