C++:用引用和值解压元组,无需复制/移动太多

toh*_*ava 1 c++ tuples reference move-semantics c++11

假设我有一个元组,t它的类型是std::tuple<T1,T2,T3>每个元组都T可以是Obj, Obj&, Obj&&, const Obj&。我想编写一个函数,将元组的值解包成一个函数f,该函数接受三种衰减到Obj. 我想尽可能避免无用的复制,我该怎么做?

我目前的实现是

static R call(C2&& c, TUP&& x) {
using TUP0 = typename std::remove_reference<TUP>::type;
return c(std::forward<typename std::tuple_element<0, TUP0>::type>(std::get<0>(x)),
         std::forward<typename std::tuple_element<1, TUP0>::type>(std::get<1>(x)),
         std::forward<typename std::tuple_element<2, TUP0>::type>(std::get<2>(x)));
}
Run Code Online (Sandbox Code Playgroud)

但这种实现看起来move当事情TUPstd::tuple<Obj,Obj,Obj>,即使当TUP包含它应该只动Obj&&

Bar*_*rry 5

在 C++17 中,这被称为std::apply()

static R call(C2&& c, TUP&& x) {
    return std::apply(std::forward<C2>(c), std::forward<TUP>(x));
}
Run Code Online (Sandbox Code Playgroud)

这可以通过使用索引序列技巧在 C++11 中实现。std::make_index_sequence仅在 C++14 中被添加到标准库中,但它本身也可以在 C++11 中实现,我不会在此处包含该实现:

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

template <class F, class Tuple>
auto apply(F&& f, Tuple&& t)
    -> decltype(details::apply_impl(std::forward<F>(f), std::forward<Tuple>(t), make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{}))
{
    return details::apply_impl(std::forward<F>(f), std::forward<Tuple>(t),
        make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{});
}
Run Code Online (Sandbox Code Playgroud)

似乎与std::get()实际执行的操作有些混淆。请注意,这取决于元组的引用限定。以下是相关的重载:

template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&
    get( tuple<Types...>& t );
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >&&
    get( tuple<Types...>&& t );
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >const&
    get( const tuple<Types...>& t );
template< std::size_t I, class... Types >
constexpr std::tuple_element_t<I, tuple<Types...> >const&&
    get( const tuple<Types...>&& t );
Run Code Online (Sandbox Code Playgroud)

返回类型与const输入元组的和 ref 限定条件相匹配。给出一个std::tuple<int> a, std::tuple<int&> b, 和std::tuple<int&&> c

std::get<0>(a);            // int&
std::get<0>(std::move(a)); // int&&

std::get<0>(b);            // int&
std::get<0>(std::move(b)); // int&, because reference collapsing

std::get<0>(c);            // int&, because reference collapsing
std::get<0>(std::move(c)); // int&&
Run Code Online (Sandbox Code Playgroud)

std::get<I>(std::forward<TUP>(x))无论元组成员的类型如何,都会为您提供正确、安全的引用类型。std::get<0>(c)给你一个左值引用 - 这是正确的行为。如果你想要一个右值引用,你需要一个右值。按照惯例。

  • 我认为 OP 的“问题”是 `std::tuple&lt;int&amp;&amp;&gt; c` 为 `std::get&lt;0&gt;(c)` 提供了 `int&amp;`,而需要 `int&amp;&amp;`。 (2认同)