std::ref(T) 和 T& 之间的 C++ 区别?
C++ Difference between std::ref(T) and T&?
我对这个程序有一些疑问:
#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
不是return一个对象的引用,那它有什么作用呢?基本上,有什么区别:
T x;
auto r = ref(x);
和
T x;
T &y = x;
另外,我想知道为什么会存在这种差异?当我们有引用(即 T&
)时,为什么我们需要 std::ref
或 std::reference_wrapper
?
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&
.
引用(T&
或T&&
)是C++语言中的一种特殊元素。它允许通过引用 操作对象 并且在语言中有特殊的用例。例如,您不能创建标准容器来保存引用:vector<T&>
格式错误并生成编译错误。
另一方面,A std::reference_wrapper
是一个能够保存引用的 C++ 对象。因此,您可以在标准容器中使用它。
std::ref
是一个标准函数,它的参数 returns 是 std::reference_wrapper
。同样的想法,std::cref
returns std::reference_wrapper
到一个常量引用。
std::reference_wrapper
的一个有趣的 属性 是它有一个 operator T& () const noexcept;
。这意味着即使它是一个真正的对象,它也可以自动转换为它持有的引用。所以:
- 因为它是可复制赋值对象,所以可以在容器中使用,或者在其他不允许引用的情况下使用
- 由于它的
operator T& () const noexcept;
,它可以在任何可以使用引用的地方使用,因为它会自动转换为它。
那么 ref
构造一个适当 reference_wrapper
类型的对象来保存对对象的引用。这意味着当您申请时:
auto r = ref(x);
这 returns 是 reference_wrapper
而不是对 x
的直接引用(即 T&
)。这个reference_wrapper
(即r
)反而持有T&
.
A reference_wrapper
当你想模拟一个可以被复制的对象的 reference
时非常有用(它既是 copy-constructible 又可复制分配).
在 C++ 中,一旦您创建了对对象(例如 x
)的引用(例如 y
),那么 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
*/
看到这里我们可以了解三件事:
一个reference_wrapper
对象(这里是r
)可以用来创建一个引用数组,而是不可能的T&
.
r
实际上就像一个真正的引用(看看 r.get()=70
如何改变 y
的值)。
r
与 T&
不同,但 r.get()
是。这意味着 r
持有 T&
即顾名思义是 围绕引用 T&
.
[=82= 的包装器]
我希望这个答案足以解释您的疑惑。
添加了一个示例来显示在绑定函数中传递 T& 和 ref(T) 参数时获得的值的差异。
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
}
我对这个程序有一些疑问:
#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
不是return一个对象的引用,那它有什么作用呢?基本上,有什么区别:
T x;
auto r = ref(x);
和
T x;
T &y = x;
另外,我想知道为什么会存在这种差异?当我们有引用(即 T&
)时,为什么我们需要 std::ref
或 std::reference_wrapper
?
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&
.
引用(T&
或T&&
)是C++语言中的一种特殊元素。它允许通过引用 操作对象 并且在语言中有特殊的用例。例如,您不能创建标准容器来保存引用:vector<T&>
格式错误并生成编译错误。
A std::reference_wrapper
是一个能够保存引用的 C++ 对象。因此,您可以在标准容器中使用它。
std::ref
是一个标准函数,它的参数 returns 是 std::reference_wrapper
。同样的想法,std::cref
returns std::reference_wrapper
到一个常量引用。
std::reference_wrapper
的一个有趣的 属性 是它有一个 operator T& () const noexcept;
。这意味着即使它是一个真正的对象,它也可以自动转换为它持有的引用。所以:
- 因为它是可复制赋值对象,所以可以在容器中使用,或者在其他不允许引用的情况下使用
- 由于它的
operator T& () const noexcept;
,它可以在任何可以使用引用的地方使用,因为它会自动转换为它。
那么 ref
构造一个适当 reference_wrapper
类型的对象来保存对对象的引用。这意味着当您申请时:
auto r = ref(x);
这 returns 是 reference_wrapper
而不是对 x
的直接引用(即 T&
)。这个reference_wrapper
(即r
)反而持有T&
.
A reference_wrapper
当你想模拟一个可以被复制的对象的 reference
时非常有用(它既是 copy-constructible 又可复制分配).
在 C++ 中,一旦您创建了对对象(例如 x
)的引用(例如 y
),那么 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
*/
看到这里我们可以了解三件事:
一个
reference_wrapper
对象(这里是r
)可以用来创建一个引用数组,而是不可能的T&
.r
实际上就像一个真正的引用(看看r.get()=70
如何改变y
的值)。
[=82= 的包装器]r
与T&
不同,但r.get()
是。这意味着r
持有T&
即顾名思义是 围绕引用T&
.
我希望这个答案足以解释您的疑惑。
添加了一个示例来显示在绑定函数中传递 T& 和 ref(T) 参数时获得的值的差异。
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
}