无法通过std :: ref()调用带有auto&parameter的std :: invoke()

nyr*_*ium 0 c++ lambda c++11 c++17

我正在尝试创建一个Invoker对象,该对象为该仿函数存储仿函数和一组参数 - 全部按值(用于线程). Invoker::operator()()将使用复制的参数调用存储的仿函数.

到目前为止,一切都工作正常,直到尝试通过auto&使用传递参数std::ref(variable).具体来说,此代码应该可以工作,但它不会使用给定的错误消息进行编译:

int var = 0;
Invoker{
    [](auto& r) {
        printf("%d\n", r);
    }, std::ref(var)
}();
Run Code Online (Sandbox Code Playgroud)

我希望它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))...);
   ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

我目前的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&&...>;
Run Code Online (Sandbox Code Playgroud)

请参阅此处以获取此问题的在线版本.错误消息表明,推导出的类型auto&std::reference_wrapper<int>&,应该是什么时候int&.不幸的是我无法想出解决这个问题的方法.

编辑:

正如评论所示,表达方式

int var = 5;
std::thread{ [](auto& r) { printf("%d\n", r); }, std::ref(var) };
Run Code Online (Sandbox Code Playgroud)

只编译gcc >= 7.1.0.我很欣赏这个主题的详细说明,特别是如果这是c ++标准的正确行为.

T.C*_*.C. 5

INVOKE一般不打开reference_wrapper论据; 它们按原样使用(有一个例外与此无关:如果你调用一个指向成员的指针reference_wrapper作为第一个参数,那么该参数是unwrapped).因此,invoke([](auto&){}, std::ref(var));将尝试使用rvalue调用lambda reference_wrapper,这对于尝试将rvalue绑定到左值引用而言是错误的.

观察到的行为std::thread是一个已修复的libstdc ++错误.简而言之,libstdc ++ std::thread将所提供的参数存储在a中tuple,该参数是(错误地)构造的make_tuple(解包reference_wrappers).