【问题标题】:std::tuple get everything except last elementstd::tuple 获取除最后一个元素之外的所有内容
【发布时间】:2018-08-12 16:26:30
【问题描述】:

我试图返回除@​​987654322@ 中最后一个元素之外的所有元素,如果元组中只有两个元素,则返回第一个元素。由于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 个错误。

【问题讨论】:

  • 你想返回一个包含除最后一个之外的所有元素的元组吗?
  • std::ignore 我猜可能对你有帮助。

标签: c++ c++17


【解决方案1】:

与您的问题无关,但模板暗示 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});
}

您在 cmets 中说您想查看 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});
}

【讨论】:

  • constexpr 不是隐含的吗?无论如何,我同意,这似乎是一个很好的解决方案,但你认为我可以得到 static_assert(init(std::tuple{1,2}) == 1) 而不是一元元组吗?
  • @rausted 不, constexpr 不是隐式的。 (我认为自 c++17 以来的 lambdas 除外)。是的,你可以得到那个,但你确定你想要那个吗?这将是不一致的。我会针对这种情况更新我的答案。
  • 我同意这是不一致的,因为现在 init 可以返回 std::tuple&lt;Args...&gt; 和通用 T 但如果可能的话,它是一个有趣的元编程中依赖类型的实验。
【解决方案2】:

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

#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

【讨论】:

  • 你为什么用::std
  • @Evgeny 在全局命名空间中引用命名空间std
  • 我明白这一点。但是如果只是std 会出什么问题呢?我什么时候应该使用::std 而不是std
  • @Evgeny 标识符 std 不是特殊的或保留的。只是std 它可能指的是在封闭范围内声明的东西(例如,另一个命名空间或类或枚举器)。一般来说,在全局命名空间中始终使用 :: 作为前缀是一个好主意,这样它们就不会突然改变它们的含义。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-01-05
  • 2018-07-11
  • 1970-01-01
  • 2020-01-25
  • 2011-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多