【问题标题】:perfect forwarding failing for lvalues左值的完美转发失败
【发布时间】:2015-02-24 21:48:56
【问题描述】:

我有一个实用函数,它遍历一个元组,并且对于每个元素,使用该元素调用一个函数,最后使用所有元组元素的结果调用另一个函数。

为了更好地说明:

  • 有一个各种类型的元组tuple<Foo<int>, Bar<double>, Baz<char>>
  • FooBarBaz 每种类型都有一个隐式接口 ClassT::data(),它返回对某个内部成员 T& 的引用。
  • 你有一个签名为void (int, double, char)的函数

该实用程序遍历元组成员,提取对内部成员的引用,并使用所需参数调用函数。

问题:完美转发

我尝试使用所谓的“通用引用”和完美转发来实现该实用程序,从而消除了对多个左值/右值和 const/non-const 重载的需要。

虽然函数的参数是类型

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

我无法将左值绑定到它。这是为什么呢?

我在here 之前问过一个类似的问题,这个问题已经解决了;但是,由于这是一个不相关的问题,我认为这需要一个新问题

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;

}

【问题讨论】:

  • the one with the error 比没有任何错误的更有用。 :)
  • @Yakk - 哈,很公平 - 我会更新问题
  • 哇,反对票是怎么回事?这个问题到底有什么问题?

标签: c++ c++11 perfect-forwarding stdtuple


【解决方案1】:
template<typename Func, typename ForEachFunc, typename... Args>
static auto apply(Func&& func, ForEachFunc&& forEachFunc, std::tuple<Args...>&& args)

第三个参数是右值tuple,不是转发引用的元组,也不是对元组的转发引用。

通用引用(目前在开发中的 C++1z 标准中称为“转发引用”)通常需要推导类型。它们确实要求应用&amp;&amp; 的类型可以是引用类型,也可以是非引用类型。 std::tuple&lt;?&gt; 始终是非引用类型,因此 &amp;&amp; 在像 std::tuple&lt;?&gt;&amp;&amp; 这样放置时使其成为右值引用,而不是转发引用。

您从Tuple&amp;&amp; 切换到std::tuple&lt;?&gt;&amp;&amp; 似乎只是为了计算Args... 的数量。如果我是对的,那么std::tuple_size&lt;std::decay_t&lt;Tuple&gt;&gt;{} 等于sizeof...(Args)

【讨论】:

    【解决方案2】:

    问题在于您的 apply 函数 - 它需要 rvalue 类型为 std::tuple&lt;Args...&gt;

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

    以下是您的代码中使用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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-26
      • 1970-01-01
      • 2021-06-17
      • 1970-01-01
      • 1970-01-01
      • 2018-08-08
      • 2020-09-12
      相关资源
      最近更新 更多