c++ 模板替换过程中参数包中的索引

cu6pst1q  于 2024-01-09  发布在  其他
关注(0)|答案(2)|浏览(152)

我在C++17中有一个模板函数,其中第N个参数需要是整型,这应该在模板替换过程中检查。(模板函数不应该为不满足条件的参数而存在,所以函数体中的static_assert()不起作用。)
它是类的构造函数,所以SFINAE是在模板参数中完成的。
对于固定的N = 2,这起作用:

  1. struct A {
  2. template<class Arg1, class Arg2, class... OtherArgs,
  3. std::enable_if_t<(std::is_integral_v<Arg1> && std::is_integral_v<Arg2>), int> = 0
  4. >
  5. A(Arg1 arg1, Arg2 arg2, OtherArgs... otherArgs)
  6. };

字符串
然而,当N是一个模板参数时,似乎没有明显的方法。
要检查 * 所有 * 参数是否都是积分,可以这样做:

  1. template<std::size_t N>
  2. struct A {
  3. template<class... Args,
  4. std::enable_if_t<std::conjunction_v<std::is_integral<Args>...>, int> = 0
  5. >
  6. A(Args... args)
  7. };


这里有没有像INDEX_OF这样的工具,它可以获取参数包中参数的索引,这样is_integral只需要对前N个参数为真?

  1. template<std::size_t N>
  2. struct A {
  3. template<class... Args,
  4. std::enable_if_t<std::conjunction_v<
  5. std::disjunction<
  6. std::bool_constant< (INDEX_OF(Args) >= N) >,
  7. std::is_integral<Args>
  8. >...
  9. >, int> = 0
  10. >
  11. A(Args... args)
  12. };

5lhxktic

5lhxktic1#

你可以创建constexpr函数:

  1. template <std::size_t N, typename... Ts>
  2. static constexpr bool first_are_integral()
  3. {
  4. constexpr std::array<bool, sizeof...(Ts)> bools{std::is_integral_v<Ts>...};
  5. for (std::size_t i = 0; i != N; ++i) {
  6. if (!bools[i]) {
  7. return false;
  8. }
  9. }
  10. return true;
  11. // C++20
  12. // return std::all_of(std::begin(bools), std::begin(bools) + N, [](bool b){ return b; });
  13. }

字符串
然后

  1. template <class... Args,
  2. std::enable_if_t<first_are_integral<N, Args...>(), int> = 0
  3. >
  4. A(Args...){ /*...*/ }


Demo
或者,std::index_sequence是一种拥有索引和类型的方法(来自元组)

  1. template <typename Seq, typename Tuple>
  2. struct first_are_integral_impl;
  3. template <std::size_t...Is, typename Tuple>
  4. struct first_are_integral_impl<std::index_sequence<Is...>, Tuple>
  5. {
  6. constexpr static bool value =
  7. (std::is_integral_v<std::tuple_element_t<Is, Tuple>> && ...);
  8. };
  9. template <std::size_t N, typename Tuple>
  10. constexpr bool first_are_integral_v =
  11. first_are_integral_impl<std::make_index_sequence<N>, Tuple>::value;


Demo

展开查看全部
7eumitmz

7eumitmz2#

您可以创建一个辅助模板:

  1. template <std::size_t N, typename... T>
  2. inline constexpr bool conjunction_n_v = [] {
  3. std::size_t i = 0;
  4. return ((i++ >= N || T::value) && ...);
  5. }();
  6. // ...
  7. static_assert( conjunction_n_v<1, std::is_integral<int>, std::is_integral<float>>);
  8. static_assert(!conjunction_n_v<2, std::is_integral<int>, std::is_integral<float>>);

字符串

这有点像std::conjunction,但不会短路模板示例化。

替代方案

如果示例化是贪婪的,那么我们也可以使用一组bool

  1. template <std::size_t N, bool... B>
  2. inline constexpr bool first_n_true_v = [] {
  3. std::size_t i = 0;
  4. return ((i++ >= N || B) && ...);
  5. }();

保持std::conjunction短循环示例化

为了提高编译速度,也只是出于必要,std::conjunction短路并且不示例化所有给定类型的事实是有用的。这可以使用经典的递归模板来实现:

  1. template <std::size_t N, typename... T>
  2. struct conjunction_n : std::false_type {};
  3. template <std::size_t N, typename Head, typename... Tail>
  4. struct conjunction_n<N, Head, Tail...>
  5. : std::conjunction<std::disjunction<std::bool_constant<N == 0>, Head>,
  6. conjunction_n<N - 1, Tail...>>
  7. {};
  8. template <std::size_t N, typename... T>
  9. inline constexpr bool conjunction_n_v = conjunction_n<N, T...>::value;

展开查看全部

相关问题