简体   繁体   English

左值的完美转发失败

[英]perfect forwarding failing for lvalues

I have a utility function which iterates over a tuple, and for each element, calls a function with that element, and finally calls another function with the result of all the tuple elements. 我有一个实用函数,它遍历一个元组,并且对于每个元素,使用该元素调用一个函数,最后使用所有元组元素的结果调用另一个函数。

To better illustrate: 为了更好地说明:

  • There is a tuple of various types tuple<Foo<int>, Bar<double>, Baz<char>> 有各种类型的tuple<Foo<int>, Bar<double>, Baz<char>>
  • Each type Foo , Bar and Baz have an implicit interface ClassT::data() which returns a reference to some internal member T& . 每个FooBarBaz类型都有一个隐式接口ClassT::data() ,该接口返回对某些内部成员T&的引用。
  • You have a function with the signature void (int, double, char) 您具有签名为void (int, double, char)的函数void (int, double, char)

The utility iterates over the tuple members, extracting the reference to the internal members, and calls the function with the required parameters. 该实用程序遍历元组成员,提取对内部成员的引用,并使用所需的参数调用该函数。

Issue: perfect forwarding 问题:完美转发

I have tried to implement the utility using so-called "universal references" and perfect forwarding, thereby negating the need for multiple lvalue/rvalue and const/non-const overloads. 我试图使用所谓的“通用引用”和完美的转发来实现该实用程序,从而消除了对多个左值/右值和const /非const重载的需求。

Whilst the parameter to the function is of type 虽然函数的参数是类型

template<typename Tuple>
auto invoke(Tuple&& tuple)

I cannot bind lvalues to it. 我不能将左值绑定到它。 Why is this? 为什么是这样?

I have asked a similar question leading up to this here which has been solved; 我问过一个类似的问题,导致这里已经解决; however since this is an unrelated issue, I believe this warrants a new question 但是,由于这是一个无关紧要的问题,我相信这值得一个新的问题

Example on ideone: https://ideone.com/lO5JOB 关于ideone的示例: https ://ideone.com/lO5JOB

#include <tuple>
#include <iostream>

// sequence

template<size_t...>
struct Sequence
{ };

template<size_t N, size_t... Seq>
struct GenerateSequence : GenerateSequence<N - 1, N - 1, Seq...>
{ };

template<size_t... Seq>
struct GenerateSequence<0, Seq...>
{
    using type = Sequence<Seq...>;
};

// invoke tuple

struct TupleForEachInvoker
{
    template<typename Func, typename ForEachFunc, typename Tuple, size_t... Seq>
    static auto invoke(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple, Sequence<Seq...>)
        -> decltype(func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...))
    {
        return func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...);
    }

    template<typename Func, typename ForEachFunc, typename... Args>
    static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)
        -> decltype(invoke(std::forward<Func>(func),
                           std::forward<ForEachFunc>(forEachFunc),
                           std::forward<std::tuple<Args...>>(args),
                           typename GenerateSequence<sizeof...(Args)>::type()))
    {
        return invoke(std::forward<Func>(func),
                      std::forward<ForEachFunc>(forEachFunc),
                      std::forward<std::tuple<Args...>>(args),
                      typename GenerateSequence<sizeof...(Args)>::type());
    }
};

template<typename Func, typename ForEachFunc, typename Tuple>
inline auto invokeWithMemberFromAll(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple)
    -> decltype(TupleForEachInvoker::apply(std::forward<Func>(func),
                                           std::forward<ForEachFunc>(forEachFunc),
                                           std::forward<Tuple>(tuple)))
{
    return TupleForEachInvoker::apply(std::forward<Func>(func),
                                      std::forward<ForEachFunc>(forEachFunc),
                                      std::forward<Tuple>(tuple));
}

// exemplar

template<typename T>
struct Foo
{
    T& data() { return _val; }
    T _val;
};

struct Extract
{
    template<typename T>
    T& operator() (Foo<T>& f) { return f.data(); }
};

int main()
{
    Foo<int>         i { 5 };
    Foo<double>      d { 6. };
    Foo<const char*> s { "hello world" };

    auto cb = [](int& i, const double& d, const char* s)
        {
            std::cout << "i=" << i << ", d=" << d << ", s=" << s << std::endl;

            i += 2;
        };


    // rvalue reference to tuple
    invokeWithMemberFromAll(cb, Extract{}, std::tie(i, d, s));

    std::cout << i.data() << std::endl;

    // lvalue reference to tuple - fails
    auto tuple = std::tie(i, d, s);
    invokeWithMemberFromAll(cb, Extract{}, tuple);

    std::cout << i.data() << std::endl;

}
template<typename Func, typename ForEachFunc, typename... Args>
static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)

the 3rd argument is an rvalue tuple , not a tuple of forwarding references, or a forwarding reference to a tuple. 第三个参数是右值tuple ,不是转发引用的元tuple ,也不是对元组的转发引用。

Universal references (currently called "forwarding references" in the under development C++1z standard) usually require a deduced type. 通用引用(在开发中的C ++ 1z标准中当前称为“转发引用”)通常需要推导的类型。 They do require that the type the && is applied to could be either a reference or a non-reference type. 他们确实要求将&&应用于的类型可以是引用类型,也可以是非引用类型。 A std::tuple<?> is always a non-reference type, so && makes it an rvalue reference, not a forwarding reference, when placed like std::tuple<?>&& . std::tuple<?>始终是非引用类型,因此,当与std::tuple<?>&&放置时, &&使其成为右值引用,而不是转发引用。

It appears you switch from Tuple&& to std::tuple<?>&& for no reason other than to count the number of Args... . 看来您从Tuple&&切换到std::tuple<?>&&只是为了计算Args...的数量。 If I'm right, then std::tuple_size<std::decay_t<Tuple>>{} equals sizeof...(Args) . 如果我是对的,则std::tuple_size<std::decay_t<Tuple>>{}等于sizeof...(Args)

The problem is with your apply function - it requires an rvalue of type std::tuple<Args...> : 问题出在您的apply函数上-它需要一个std::tuple<Args...>类型的rvalue

template<typename Func, typename ForEachFunc, typename... Args>
    static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)

Here's one possible fix using std::tuple_size instead of sizeof...(Args) from your code: 这是使用std::tuple_size代替代码中的sizeof...(Args)的一种可能的解决方法:

struct TupleForEachInvoker
{
    template<typename Func, typename ForEachFunc, typename Tuple, size_t... Seq>
    static auto invoke(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple, Sequence<Seq...>)
        -> decltype(func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...))
    {
        return func(forEachFunc(std::get<Seq>(std::forward<Tuple>(tuple)))...);
    }

    template<typename Func, typename ForEachFunc, typename Tuple>
    static auto apply(Func&& func, ForEachFunc&& forEachFunc, Tuple&& args)
        -> decltype(invoke(std::forward<Func>(func),
                           std::forward<ForEachFunc>(forEachFunc),
                           std::forward<Tuple>(args),
                           typename GenerateSequence<std::tuple_size<std::decay_t<Tuple>>::value>::type()))
    {
        return invoke(std::forward<Func>(func),
                      std::forward<ForEachFunc>(forEachFunc),
                      std::forward<Tuple>(args),
                      typename GenerateSequence<std::tuple_size<std::decay_t<Tuple>>::value>::type());
    }
};

template<typename Func, typename ForEachFunc, typename Tuple>
inline auto invokeWithMemberFromAll(Func&& func, ForEachFunc&& forEachFunc, Tuple&& tuple)
    -> decltype(TupleForEachInvoker::apply(std::forward<Func>(func),
                                           std::forward<ForEachFunc>(forEachFunc),
                                           std::forward<Tuple>(tuple)))
{
    return TupleForEachInvoker::apply(std::forward<Func>(func),
                                      std::forward<ForEachFunc>(forEachFunc),
                                      std::forward<Tuple>(tuple));
}

Live Demo 现场演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM