无法使用auto&调用std :: invoke()参数via std :: ref()

时间:2018-02-23 13:29:52

标签: c++ c++11 lambda c++17

我正在尝试创建一个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 ++标准的正确行为。

1 个答案:

答案 0 :(得分:5)

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

std::thread观察到的行为是libstdc++ bug,已修复。简而言之,libstdc ++的std::thread将提供的参数存储在tuple中,该make_tuple({错误地)用reference_wrapper构建(解包WITH CTE AS (SELECT Row_number() OVER ( partition BY Month(AffectedDate) ORDER BY AffectedDate DESC) rn, [ItemKey], [Location], [QtyOnHand], [UpdatedOnHandQty], AffectedDate FROM [Test].[dbo].[INCos] WHERE ItemKey = '20406') SELECT * FROM CTE WHERE rn = 1 s)。