【问题标题】:How does std::apply forward parameters without explicit std::forward?没有显式 std::forward 的 std::apply 参数如何?
【发布时间】:2016-12-21 10:34:34
【问题描述】:

考虑std::apply的可能实现:

namespace detail {
template <class F, class Tuple, std::size_t... I>
constexpr decltype(auto) apply_impl(F &&f, Tuple &&t, std::index_sequence<I...>) 
{
    return std::invoke(std::forward<F>(f), std::get<I>(std::forward<Tuple>(t))...);
}
}  // namespace detail

template <class F, class Tuple>
constexpr decltype(auto) apply(F &&f, Tuple &&t) 
{
    return detail::apply_impl(
        std::forward<F>(f), std::forward<Tuple>(t),
        std::make_index_sequence<std::tuple_size_v<std::decay_t<Tuple>>>{});
}

为什么在调用函数(f) 和参数元组传递(t) 时,我们不需要在实现中对元组std::get&lt;I&gt;(std::forward&lt;Tuple&gt;(t))... 的每个元素执行std::forward

【问题讨论】:

    标签: c++ c++17 perfect-forwarding stdapply


    【解决方案1】:

    您不需要std::forward 每个元素,因为std::get 被重载用于元组的右值引用和左值引用。

    std::forward&lt;Tuple&gt;(t) 会给你一个左值(Tuple &amp;)或一个右值(Tuple &amp;&amp;),根据你得到的,std::get 会给你一个T &amp;(左值)或一个@987654334 @(右值)。查看std::get的各种重载。


    关于std::tuplestd::get的一些细节-

    正如StoryTeller 所提到的,元组的每个成员都是一个左值,无论它是从右值还是左值构造的,在这里都无关紧要:

    double a{0.0};
    auto t1 = std::make_tuple(int(), a);
    auto t2 = std::make_tuple(int(), double());
    

    问题是 - 元组是右值吗?如果是,你可以移动它的成员,如果不是,你必须做一个复制,但是std::get已经通过返回相应类别的成员来处理这个问题。

    decltype(auto) a1 = std::get<0>(t1);
    decltype(auto) a2 = std::get<0>(std::move(t1));
    
    static_assert(std::is_same<decltype(a1), int&>{}, "");
    static_assert(std::is_same<decltype(a2), int&&>{}, "");
    

    回到std::forward 的具体示例:

    template <typename Tuple>
    void f(Tuple &&tuple) { // tuple is a forwarding reference
        decltype(auto) a = std::get<0>(std::forward<Tuple>(tuple));
    }
    
    f(std::make_tuple(int())); // Call f<std::tuple<int>>(std::tuple<int>&&);
    std::tuple<int> t1;
    f(t1); // Call f<std::tuple<int>&>(std::tuple<int>&);
    

    f的第一次调用中,a的类型将是int&amp;&amp;,因为tuple将作为std::tuple&lt;int&gt;&amp;&amp;转发,而在第二种情况下,它的类型将是int&amp;,因为@ 987654349@ 将作为std::tuple&lt;int&gt;&amp; 转发。

    【讨论】:

    • 如果我们传递给get std::tuple&lt;int&amp;&amp;&gt;&amp; 然后在get 之后我们会完善转发结果元素?会一样吗?
    • you cannot create a std::tuple of reference?那么std::forward_as_tuple 在做什么呢?
    • 当然你可以创建一个引用元组ideone.com/rm8rAZ
    • 你永远不会得到 std::get 的副本,接下来会发生什么取决于表达式的使用方式
    • @W.F.也许你应该稍微改变一下你的心态,将std::forward 视为条件std::move,将std::move 视为普通演员,仅此而已。上面的答案至少对我来说很难解析,尤其是 “你不能转发不是转发参考的东西” 部分
    【解决方案2】:

    std::forward 用于确保所有内容都以正确的值类别到达调用站点。

    但元组的每个成员都是左值,即使它是 rvalue 引用的元组。

    【讨论】:

    • 正是我的观点,我们可能希望将右值引用传递给函数f
    • 即使转发将在 std::invoke 内部完成,该函数也无法知道参数是否为右值引用,不是吗?
    • @W.F. - 我们会知道的。但就像 Holt 阐述的那样,我们唯一可以假设临时绑定到元组中的右值引用仍然是一个有效的对象,是元组本身是否绑定到一个右值引用。因此,元组本身的值类别应该总是胜出。
    • @W.F. - 如果我之前的示例写得不好,请提供插图:coliru.stacked-crooked.com/a/9bcc65c3929968cf
    • 是的,在使用引用时我们需要小心......但这可能真的很节省时间......
    猜你喜欢
    • 2016-03-21
    • 2022-01-05
    • 2015-05-22
    • 2015-07-20
    • 2016-07-08
    • 2014-01-04
    • 1970-01-01
    • 2011-11-07
    • 2014-06-12
    相关资源
    最近更新 更多