在参数包和可变参数模板上解包元组的语法

时间:2021-04-04 18:49:54

标签: c++ tuples variadic-templates c++20

我有一个示例参数包函数:

lw

我经常使用 template<typename T, typename ... Args> constexpr bool all_values_equal(T first, Args... args) { return ((first == args) && ...); } static_assert(all_values_equal(1, 1, 1.0)); ,因为我喜欢能够使用模板。提取类型、切片类型等排列方式。

如何使用 std::tuple 调用此函数?

std::tuple

我也想知道不带参数的可变参数模板函数的情况:

template<typename ... Args>
constexpr bool all_values_equal(std::tuple<Args...> tuple) {
    return /* ??? */ ;
}

static_assert(all_values_equal(std::make_tuple(1, 1, 1.0)));

最终我希望能够像这样调用所有 4 个变体:

template<typename T, typename ... Args>
constexpr bool all_types_equal() {
    return (std::is_same_v<T, Args> && ...);
}

template <template<typename ... Args> class Tuple>
constexpr bool all_types_equal() {
    return /* ??? */ ;
}

static_assert(all_types_equal<int, int>());
static_assert(all_types_equal<std::tuple<int, int>>());

static_assert(all_values_equal(1, 1, 1.0)); static_assert(all_values_equal(std::make_tuple(1, 1, 1.0))); static_assert(all_types_equal<int, int>()); static_assert(all_types_equal<std::tuple<int, int>>()); 函数不应重新实现可变参数模板函数的逻辑。 我怎样才能以干净和现代的方式实现这一目标?

2 个答案:

答案 0 :(得分:1)

std::apply 让我们以元组元素为参数调用一个函数。您可以将其与 lambda 结合使用。

template<typename ... Args>
constexpr bool all_values_equal(std::tuple<Args...> tuple) {
    auto cmp = [](auto&& first, auto&&... args) {
        return ((first == args) && ...);
    };

    return std::apply(cmp, tuple);
}

为了进行 all_types_equal 检查,我们可以使用偏特化。像这样的东西。

template <typename First, typename ... Rest>
constexpr bool all_types_equal_impl = (std::is_same_v<First, Rest> && ...);;

template <typename First, typename ... Rest>
constexpr bool all_types_equal_impl<std::tuple<First, Rest...>> = (std::is_same_v<First, Rest> && ...);

template <typename... Args>
constexpr bool all_types_equal() {
    return all_types_equal_impl<Args...>;
}

我们可以直接引用模板变量,所以如果我们不想的话,实际上不需要将它包装在一个函数中。

答案 1 :(得分:0)

使用 std::index_sequence 允许访问每个数据字段和 std::tuple 的每种类型。 由于可以对 C++20 lambda 进行模板化,因此可以进行内联解包。

std::tuple
  • std::make_index_sequencestd::tuple_size_v 是标准中的便捷实用程序。

  • template<typename ... Args> constexpr bool all_values_equal(std::tuple<Args...> tuple) { constexpr auto unpack_tuple = []<typename Tuple, size_t... Ints>(Tuple tuple, std::index_sequence<Ints...>) { return all_values_equal(std::get<Ints>(tuple)...); }; return unpack_tuple(tuple, std::make_index_sequence<std::tuple_size_v<decltype(tuple)>>()); } template <template<typename ... Args> class Tuple> constexpr bool all_types_equal() { constexpr auto unpack_tuple = []<typename Tuple, size_t... Ints>(std::index_sequence<Ints...>) { return all_types_equal<std::tuple_element_t<Ints, Tuple>...>(); }; return unpack_tuple.template operator()<Tuple>(std::make_index_sequence<std::tuple_size_v<Tuple>>()); } static_assert(all_values_equal(1, 1, 1.0)); static_assert(all_values_equal(std::make_tuple(1, 1, 1.0))); static_assert(all_types_equal<int, int>()); static_assert(all_types_equal<std::tuple<int, int>>()); 解包值,std::get<Int>(tuple)... 解包类型。

  • std::tuple_element_t<Ints, Tuple>... 允许指定模板化 lambda 的模板