【问题标题】:syntax to unpack tuple on parameter pack and variadic template在参数包和可变参数模板上解包元组的语法
【发布时间】:2021-06-30 19:50:02
【问题描述】:

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

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 函数不应重新实现可变参数模板函数的逻辑。 我怎样才能以干净和现代的方式实现这一目标?

【问题讨论】:

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


    【解决方案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...>;
    }
    

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

    【讨论】:

    • 我考虑过std::apply,当它未能调用可变参数函数时感到困惑,看起来你不能传递模板函数(这是有道理的)。使用 lambda 作为中间步骤是一个有趣的解决方案,谢谢
    • A) return (args == ...); 这并不像您认为的那样;它比较前两个元组元素是否相等,但随后将下一个元素与第一个比较的 result of 进行比较,这是一个布尔值,并且(由于示例的巧合,使用数字 1 条目)将“真实”与 1 进行比较(例如,对包含所有 2 个条目的元组尝试此操作),以及 B)因为 cmp lambda 不捕获任何内容,因此使用默认引用捕获是多余的(并且可能会令人困惑)。
    • lambda re 实现了不合需要的逻辑。所以应该是auto cmd = [](auto&amp;&amp;... args) { return all_values_equal(args...); }; 我也想知道是否有解决仅模板功能的解决方案(除了我写的那个)。
    • @StackDanny 添加了类型检查的示例。
    • 这已经接近我想要的,但并不完全。我编辑了问题的底部以进一步澄清。
    【解决方案2】:

    使用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_sequencestd::tuple_size_v 是标准中的便捷实用程序。

    • std::get&lt;Int&gt;(tuple)... 解包值,std::tuple_element_t&lt;Ints, Tuple&gt;... 解包类型。

    • .template operator()&lt;Tuple&gt;(...) 允许指定模板化 lambda 的模板

    【讨论】:

      猜你喜欢
      • 2017-04-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多