我正在尝试创建一个 Invoker
对象,它存储一个仿函数和该仿函数的一组参数 - 全部按值(用于线程)。
Invoker::operator()()
将使用复制的参数调用存储的仿函数。
到目前为止一切正常,直到有人试图通过 auto&
传递一个参数使用 std::ref(variable)
.具体来说,此代码应该可以工作,但它不会使用给定的错误消息进行编译:
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&
.不幸的是,我想不出解决这个问题的办法。
编辑:
如评论所示,表达式
int var = 5;
std::thread{ [](auto& r) { printf("%d\n", r); }, std::ref(var) };
仅使用 gcc >= 7.1.0
编译.我很高兴看到关于这个主题的详细说明,特别是如果这是 c++ 标准的正确行为。
最佳答案
INVOKE
通常不会解包 reference_wrapper
参数;它们按原样使用(有一个异常(exception)与此处无关:如果您使用 reference_wrapper
作为第一个参数调用指向成员的指针,则该参数将被展开)。因此,invoke([](auto&){}, std::ref(var));
将尝试使用右值 reference_wrapper
调用 lambda,这是不正确的为尝试将右值绑定(bind)到左值引用而形成。
观察到的 std::thread
行为是 libstdc++ bug已修复。简而言之,libstdc++ 的 std::thread
将提供的参数存储在一个 tuple
中,它是(错误地)用 make_tuple
构建的(它解包了 reference_wrapper
s).
关于c++ - 无法通过 std::ref() 使用 auto& 参数调用 std::invoke(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48948986/