在参数包和可变参数模板上解压元组的语法
syntax to unpack tuple on parameter pack and variadic template
我有一个示例参数包函数:
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)));
我也想知道不带参数的可变参数模板函数的情况:
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>>());
最终我希望能够像这样调用所有 4 个变体:
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::tuple
函数不应重新实现可变参数模板函数的逻辑。
我怎样才能以干净和现代的方式实现这一目标?
使用 std::index_sequence
允许访问每个数据字段和每个类型的 std::tuple
。
由于 C++20 lambda 可以模板化,这允许内联解包。
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::make_index_sequence
and std::tuple_size_v
是来自标准的便捷实用程序。
std::get<Int>(tuple)...
解包值,std::tuple_element_t<Ints, Tuple>...
解包类型。
.template operator()<Tuple>(...)
允许指定模板化 lambda 的模板
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...>;
}
我们可以直接引用模板变量,所以如果我们不想的话,并不是真的需要将它包装在一个函数中。
我有一个示例参数包函数:
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)));
我也想知道不带参数的可变参数模板函数的情况:
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>>());
最终我希望能够像这样调用所有 4 个变体:
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::tuple
函数不应重新实现可变参数模板函数的逻辑。
我怎样才能以干净和现代的方式实现这一目标?
使用 std::index_sequence
允许访问每个数据字段和每个类型的 std::tuple
。
由于 C++20 lambda 可以模板化,这允许内联解包。
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::make_index_sequence
andstd::tuple_size_v
是来自标准的便捷实用程序。std::get<Int>(tuple)...
解包值,std::tuple_element_t<Ints, Tuple>...
解包类型。.template operator()<Tuple>(...)
允许指定模板化 lambda 的模板
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...>;
}
我们可以直接引用模板变量,所以如果我们不想的话,并不是真的需要将它包装在一个函数中。