使用后置 std::index_sequence 解析可变参数类型失败

时间:2021-01-24 13:12:47

标签: c++ templates variadic-templates

假设要根据索引序列生成可变参数类型的序列,即给定一个元组

using Tuple = std::tuple<int, float, bool>;

和函数签名如下:

template <std::size_t ...Is>
void func(std::index_sequence<Is...>, std::tuple_element_t<Is, Tuple>...) {}

func(std::make_index_sequence<2>(), 2, 1.0) 这样的调用可以毫无问题地编译。

但是,如果我们要根据另一种类型计算 std::index_sequence 的大小,因此将 std::index_sequence 放在带有默认参数的参数列表的末尾,就像这样:

template <typename T, std::size_t ...Is>
void func(T, std::tuple_element_t<Is, Tuple>..., std::index_sequence<Is...> = std::make_index_sequence<some_computation_v<T>>()) {}

g++ 和 clang++ 编译成功当且仅当 std::index_sequence 的大小为 0,即对于以下签名:

template <std::size_t... Is>
void func(std::tuple_element_t<Is, Tuple>..., std::index_sequence<Is...> = std::make_index_sequence<2>()) {}

func(2, 1.0)func(2, 1.0, std::make_index_sequence<2>()) 会导致错误:

main.cpp:34:6: note:   template argument deduction/substitution failed:
main.cpp:43:16: note:   mismatched types ‘std::integer_sequence<long unsigned int, _Idx ...>’ and ‘int’
     func(2, 1.0);

这防止我通过将 T 映射到参数类型的元组并使用 std::index_sequence 扩展它来限制具有键类型 T 的可变参数模板的类型。

有什么办法可以解决这个错误或者满足上面提到的需求吗?

2 个答案:

答案 0 :(得分:3)

写一个“是类型前缀”的特征。在 requires 子句中使用它。

template<class T0>
struct is_tuple_prefix_helper{
    template<class T1>
    static std::false_type test(T1 const&);
};
template<template<class...>class Z, class...T0s>
struct is_tuple_prefix_helper<Z<T0s...>>{
    template<class T1>
    static std::false_type test(T1 const&);
    template<class...T1s>
    static std::true_type test(Z<T0s...,T1s...> const&);
};

template<class T0, class T1>
constexpr bool is_tuple_prefix_v=decltype( is_tuple_prefix_helper<T0>::test(std::declval<T1>()) )::value;

然后

using tup=std::tuple<int,char,double>

template<class...Ts> requires( is_tuple_prefix_v<std::tuple<Ts...>, tup> )
void restricted_func(Ts const&...);

应该可以。

您遇到的问题是您要求反转编译时类型映射。 C++ 不会为您反转大多数编译时类型映射;这样做通常是 HALT-hard,所以 C++ 不会尝试。它会做基本的模式匹配,仅此而已。

这允许您编写图灵完备的反演或测试,但您必须编写它。

这是基于 。早期版本的 C++ 会使用 SFINAE 和其他不太优雅的机器来做同样的事情。在

template<class...Ts,
  std::enable_if_t< is_tuple_prefix_v<std::tuple<Ts...>, tup>, bool > =true
>
void restricted_func(Ts const&...);

老实说,它的工作原理是疯狂的黑魔法。他们在 中添加 requires 是有原因的。

答案 1 :(得分:2)

std::tuple_element<Is, Tuple>::type 中,Is 是不可推导的。默认函数参数不参与推导或参数类型,因此整个包为空。

您可能仍然编写辅助函数以获得预期的 API 顺序:

template <std::size_t ...Is>
void func_impl(std::index_sequence<Is...>, std::tuple_element_t<Is, Tuple>...)
{
// ...
}

template <typename T, typename ... Ts>
auto func(T, Ts&&... args)
// possibly decltype return type for SFINAE
{
    return func_impl(std::make_index_sequence<some_computation_v<T>>(),
                     std::forward<Ts>(args)...);
}
相关问题