std :: tuple获取除最后一个元素以外的所有内容

时间:2018-08-12 16:26:31

标签: c++ c++17

我正在尝试返回std::tuple中最后一个元素以外的所有内容,如果元组中只有两个元素,请返回第一个。由于std::tuple具有许多编译时功能,因此双返回类型应该是可行的。这是我到目前为止的内容:

// Behavior
// init(std::make_tuple(1,2)) = 1
// init(std::make_tuple(1,2,3)) = (1,2)

// First case
template<class T1, class T2>
inline static T1 init(Tuple<T1, T2> t) {
    return std::get<0>(t);
}

// Second case
template<class ...Args, class S = std::make_index_sequence<sizeof...(Args) - 1>>
inline static decltype(auto) init(Tuple<Args...> t) {
    return std::apply([](const auto &item...) {
        return std::tuple_cat(std::make_tuple(std::get<S>) ... std::tuple<>);
    }, t);
}

如果我能以c ++ 17友好的方式做到这一点,那就太好了。上面的实现出现以下错误:

  

./ tuple.cpp:36:55:错误:数据包扩展不包含任何未扩展的参数包
     返回std :: tuple_cat(std :: make_tuple(std :: get)... std :: tuple <>);

     

~~~~~~~~~~~~~~~~~~~~~~~~~~~ ^^
     产生1个错误。

2 个答案:

答案 0 :(得分:2)

与您的问题无关,但模板暗含inline,并且您不希望使用static。我实际上认为static违反了ODR。为什么使用它们?您也可以使函数constexpr。进一步的改进是使用(转发)引用和std::forward_as_tuple。这是一个基本的实现:

template <class... Args, std::size_t... Is>
constexpr auto init_helper(std::tuple<Args...> tp, std::index_sequence<Is...>)
{
    return std::tuple{std::get<Is>(tp)...};
}

template <class... Args>
constexpr auto init(std::tuple<Args...> tp)
{
    return init_helper(tp, std::make_index_sequence<sizeof...(Args) - 1>{});
}
auto test()
{
    static_assert(init(std::tuple{1})       == std::tuple{});
    static_assert(init(std::tuple{1, 2})    == std::tuple{1});
    static_assert(init(std::tuple{1, 2, 3}) == std::tuple{1, 2});
}

您在评论中说,您想查看init(std::tuple{1,2})是否有可能直接返回值而不是一个值的元组。注意,我不建议这样做,因为这会使函数的行为不一致,是的,这是可能的。而C++17使这一点变得非常干净:

template <class... Args>
constexpr auto init(std::tuple<Args...> tp)
{
    if constexpr (sizeof...(Args) == 2)
        return std::get<0>(tp);
    else
        return init_helper(tp, std::make_index_sequence<sizeof...(Args) - 1>{});
}

auto test()
{
    static_assert(init(std::tuple{1})       == std::tuple{});
    static_assert(init(std::tuple{1, 2})    == 1);
    static_assert(init(std::tuple{1, 2, 3}) == std::tuple{1, 2});
}

答案 1 :(得分:1)

该想法将是实现一个辅助函数,该函数将具有要复制的源元组项的索引列表:

#include <tuple>
#include <utility>
#include <cstddef>

template<typename x_Tuple, ::std::size_t... x_index> auto
make_tuple_helper(x_Tuple const & other, ::std::index_sequence<x_index...>)
{
    return ::std::make_tuple(::std::get<x_index>(other)...);
}

template<typename... x_Field> inline auto
cut_last_item(::std::tuple<x_Field...> const & other)
{
    return make_tuple_helper(other, ::std::make_index_sequence<sizeof...(x_Field) - ::std::size_t{1}>{});
}

template<> inline auto
cut_last_item(::std::tuple<> const & other)
{
    return other;
}

int main()
{
    ::std::tuple<int, short, float, double> t4{};
    ::std::tuple<int, short, float> t3{cut_last_item(t4)};
    ::std::tuple<int, short> t2{cut_last_item(t3)};
    ::std::tuple<int> t1{cut_last_item(t2)};
    ::std::tuple<> t0{cut_last_item(t1)};
    ::std::tuple<> t00{cut_last_item(t0)};
}

online compiler