C++ std::ref() 函数使用详解
Posted zpf1813763637
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ std::ref() 函数使用详解相关的知识,希望对你有一定的参考价值。
std::ref()
是C++标准库中的一个函数,定义在<functional>
头文件中,它的作用是将一个对象转换成一个引用包装器(reference wrapper),以便在函数模板中使用。
std::ref()
函数接受一个对象作为参数,并返回一个引用包装器。引用包装器是一个类模板std::reference_wrapper
的实例,它的主要作用是将一个对象转换成一个引用类型,并提供了访问该对象的引用的方法。需要注意的是,std::reference_wrapper
并不是一个裸引用,它本身是一个对象,可以被复制和赋值。
std::ref()
函数的作用在于,在函数模板中可以使用引用参数,而不是拷贝参数,这通常可以提高代码的性能。例如,下面的代码演示了如何使用std::ref()
来传递一个引用参数:
#include <iostream>
#include <functional>
void increment(int& x)
++x;
int main()
int x = 10;
std::function<void()> f = std::bind(increment, std::ref(x));
f();
std::cout << "x = " << x << std::endl; // output: x = 11
return 0;
在这个例子中,std::ref()
函数将x
对象转换成了一个引用包装器,并将其传递给std::bind()
函数,以便在函数对象f
中使用。increment()
函数接受一个引用参数,通过使用std::ref()
可以将该引用传递给函数对象,从而使函数能够修改x
的值。最后,输出结果表明,x
的值已经被增加到了11
。
需要注意的是,使用std::ref()
函数时,必须保证被转换的对象的生命周期要长于引用包装器的使用期限,否则会导致未定义的行为。另外,引用包装器的使用也可能会导致代码可读性降低,因此需要谨慎使用。
无法通过 std::ref() 调用带有 auto& 参数的 std::invoke()
【中文标题】无法通过 std::ref() 调用带有 auto& 参数的 std::invoke()【英文标题】:Cannot call std::invoke() with auto& parameter via std::ref() 【发布时间】:2018-02-23 13:29:52 【问题描述】:我正在尝试创建一个Invoker
对象,它同时存储一个函子和该函子的一组参数 - 全部按值(用于线程)。
Invoker::operator()()
将使用复制的参数调用存储的仿函数。
到目前为止一切正常,直到有人尝试使用std::ref(variable)
通过auto&
传递参数。具体来说,这段代码应该可以工作,但它不会使用给定的错误消息进行编译:
int var = 0;
Invoker
[](auto& r)
printf("%d\n", r);
, std::ref(var)
();
我希望它的工作方式类似于 std::thread
在此示例中的工作方式。
错误信息是:
test.cpp:65:14: error: no matching function for call to ‘invoke(std::__tuple_element_t<0, std::tuple<main()::<lambda(auto:1&)>, std::reference_wrapper<int> > >, std::__tuple_element_t<1, std::tuple<main()::<lambda(auto:1&)>, std::reference_wrapper<int> > >)’ std::invoke(std::get<Indicies>(std::move(args))...); ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
我现在的Invoker
班级:
template<typename... Args>
struct Invoker
std::tuple<std::decay_t<Args>...> args;
explicit Invoker(Args... args)
: args(std::forward<Args>(args)...)
template<size_t... Indices>
void _Invoke(std::index_sequence<Indices...>)
std::invoke(std::get<Indices>(std::move(args))...);
void operator()()
_Invoke(std::make_index_sequence<std::tuple_size_v<decltype(args)>>);
;
/* Invoker deduction guide (perfectly forward any parameters with full type!) */
template<typename Function, typename... Args>
Invoker(Function&&, Args&&...) -> Invoker<Function&&, Args&&...>;
See here 获取此问题的在线版本。错误消息表明,auto&
的推导类型是 std::reference_wrapper<int>&
,而它应该是 int&
。不幸的是,我无法想出解决这个问题的办法。
编辑:
如 cmets 所示,表达式
int var = 5;
std::thread [](auto& r) printf("%d\n", r); , std::ref(var) ;
仅使用 gcc >= 7.1.0
编译。我很高兴看到有关此主题的详细说明,特别是如果这是 c++ 标准的正确行为。
【问题讨论】:
auto
的推导使用相同的模板参数推导规则,这不允许隐式转换。可以使用int n = 5; auto& r = ref(n);
重现该问题。由于ref(n)
是一个右值,它不能绑定到左值引用。
@0x499602D2 尽管如此,当使用std::thread
时它可以正常工作。我想复制这种行为
@nyronium 不,std::thread([] (auto &ref) , std::ref(var));
从不编译。
@liliscent 你错了。见proof。
报告为gcc.gnu.org/bugzilla/show_bug.cgi?id=84532
【参考方案1】:
INVOKE
通常不会打开 reference_wrapper
参数;它们按原样使用(这里有一个不相关的例外:如果您调用指向成员的指针,并以 reference_wrapper
作为第一个参数,则该参数被解包)。因此,invoke([](auto&), std::ref(var));
将尝试使用右值 reference_wrapper
调用 lambda,因为尝试将右值绑定到左值引用是错误的。
观察到的std::thread
行为是已修复的libstdc++ bug。简而言之,libstdc++ 的std::thread
将提供的参数存储在tuple
中,该make_tuple
(错误地)构造了make_tuple
(它解开了reference_wrapper
s)。
【讨论】:
以上是关于C++ std::ref() 函数使用详解的主要内容,如果未能解决你的问题,请参考以下文章