c++ - 模板类型略有变化后继承特征类

标签 c++ templates variadic-templates c++17 template-meta-programming

给定

template <int...> struct Z;   template <int...> struct Q;
template <std::size_t...> struct I;

假设我们想要

accumulated_sums<Z<1,2,3,4,5>, Q>::type

成为

Q<1,3,6,10,15>

accumulated<I<1,2,3,4,5>, std::integer_sequence>::type

成为

std::index_sequence<1,3,6,10,15>

有没有办法定义 accumulated通过来自 accumulated_sums 的一些继承方案进行分类类(class)?它们的操作方式完全相同,唯一的区别是模板类型 template <T...> class对于 accumulated_sums与略有不同的 template <typename U, U...> class对于 accumulated .否则,我必须分别定义这两个类,即使它们的定义本质上是相同的。应该有某种方法可以为两个类定义一次。这是我对这两个类的完全编译代码,您可以看到它们在代码上基本相同。

#include <iostream>
#include <type_traits>
#include <utility>

namespace detail {
    template <typename Pack> struct sequence_traits;

    template <typename T, template <T...> class Z, T... Is>
    struct sequence_traits<Z<Is...>> {
        using type = T;
        template <T... Js>
        using templ_type = Z<Js...>;
    };
}

// accumulated_sums
template <typename T, typename Output, template <T...> class, T...> struct accumulated_sums_h;

template <typename T, template <T...> class Z, template <T...> class Q, T Sum, T... Is>
struct accumulated_sums_h<T, Z<Sum, Is...>, Q> {
    using type = Q<Is..., Sum>;
};

template <typename T, template <T...> class Z, T Sum, T... Is, template <T...> class Q, T Next, T... Rest>
struct accumulated_sums_h<T, Z<Sum, Is...>, Q, Next, Rest...> :
    accumulated_sums_h<T, Z<Sum + Next, Is..., Sum>, Q, Rest...> {};

template <typename Sequence,
    template <typename detail::sequence_traits<Sequence>::type...> class = detail::sequence_traits<Sequence>::template templ_type>
    struct accumulated_sums;

template <typename T, template <T...> class Z, T First, T... Rest, template <T...> class Q>
struct accumulated_sums<Z<First, Rest...>, Q> :
    accumulated_sums_h<T, Z<First>, Q, Rest...> {};

// accumulated
template <typename T, typename Output, template <typename U, U...> class, T...> struct accumulated_h;

template <typename T, template <T...> class Z, template <typename U, U...> class Q, T Sum, T... Is>
struct accumulated_h<T, Z<Sum, Is...>, Q> {
    using type = Q<T, Is..., Sum>;
};

template <typename T, template <T...> class Z, T Sum, T... Is, template <typename U, U...> class Q, T Next, T... Rest>
struct accumulated_h<T, Z<Sum, Is...>, Q, Next, Rest...> :
    accumulated_h<T, Z<Sum + Next, Is..., Sum>, Q, Rest...> {};

template <typename Sequence, template <typename U, U...> class Q> struct accumulated;

template <typename T, template <T...> class Z, T First, T... Rest, template <typename U, U...> class Q>
struct accumulated<Z<First, Rest...>, Q> :
    accumulated_h<T, Z<First>, Q, Rest...> {};

// Testing
template <int...> struct Z;
template <int...> struct Q;
template <std::size_t...> struct I;

int main() {
    std::cout << std::boolalpha << std::is_same<
        accumulated_sums<Z<1,2,3,4,5>, Q>::type,
        Q<1,3,6,10,15>
    >::value << '\n';  // true

    std::cout << std::is_same<
        accumulated_sums<Z<1,2,3,4,5>>::type,
        Z<1,3,6,10,15>
    >::value << '\n';  // true

    std::cout << std::is_same<
        accumulated<Z<1,2,3,4,5>, std::integer_sequence>::type,
        std::integer_sequence<int, 1,3,6,10,15>
    >::value << '\n';  // true

    std::cout << std::is_same<
        accumulated<I<1,2,3,4,5>, std::integer_sequence>::type,
        std::index_sequence<1,3,6,10,15>
    >::value << '\n';  // true
}

最佳答案

如果您接受通过 Q<>而不是 Qstd::integer_sequence<int> (或 std::integer_sequence<std::size_t> )而不是 std::integer_sequence (因此模板类型而不是模板模板类型)您可以在底部(请参阅以下示例中的 accumulated_h2)而不是在顶部 fork 案例(有或没有第一类型模板)

所以你可以使用accumulated对于这两种情况并扔掉accumulated_sum .

以下是一个完整的工作示例。

#include <type_traits>
#include <utility>

namespace detail
 {
   template <typename Pack>
   struct sequence_traits;

   template <typename T, template <T...> class Z, T... Is>
   struct sequence_traits<Z<Is...>>
    { using templ_empty = Z<>; };
 }

// accumulated

template <typename T, typename, T...>
struct accumulated_h2;

template <typename T, template <typename, T ...> class Q, T ... Ts>
struct accumulated_h2<T, Q<T>, Ts...>
 { using type = Q<T, Ts...>; };

template <typename T, template <T ...> class Q, T ... Ts>
struct accumulated_h2<T, Q<>, Ts...>
 { using type = Q<Ts...>; };

template <typename T, typename, typename, T...>
struct accumulated_h;

template <typename T, template <T...> class Z, typename C, T Sum, T... Is>
struct accumulated_h<T, Z<Sum, Is...>, C>
 { using type = typename accumulated_h2<T, C, Is..., Sum>::type; };

template <typename T, template <T...> class Z, T Sum, T... Is,
          typename C, T Next, T... Rest>
struct accumulated_h<T, Z<Sum, Is...>, C, Next, Rest...>
   : accumulated_h<T, Z<Sum + Next, Is..., Sum>, C, Rest...>
 { };

template <typename T,
          typename = typename detail::sequence_traits<T>::templ_empty>
struct accumulated;

template <typename T, template <T...> class Z, T First,
          T... Rest, typename C>
struct accumulated<Z<First, Rest...>, C>
   : accumulated_h<T, Z<First>, C, Rest...>
 { };

// Testing

template <int...>
struct Z;

template <int...>
struct Q;

template <std::size_t...>
struct I;

int main ()
 {
   static_assert(std::is_same<
      accumulated<Z<1,2,3,4,5>, Q<>>::type,
      Q<1,3,6,10,15>>::value, "!");
   static_assert(std::is_same<
      accumulated<Z<1,2,3,4,5>>::type,
      Z<1,3,6,10,15>>::value, "!");    
   static_assert(std::is_same<
      accumulated<Z<1,2,3,4,5>, std::integer_sequence<int>>::type,
      std::integer_sequence<int, 1,3,6,10,15>>::value, "!");
   static_assert(std::is_same<
      accumulated<I<1,2,3,4,5>, std::integer_sequence<std::size_t>>::type,
      std::index_sequence<1,3,6,10,15>>::value, "!");
 }

-- 编辑 --

OP 询问

And how to fork the cases so that accumulated<std::integer_sequence<T, 1,2,3,4,5>>::type is std::integer_sequence<T, 1,3,6,10,15>, where T is any integral type?

我看过你的解决方案,我准备了另一个,差别不大:扔掉旧的 Z ,我用 std::integer_sequence 代替了它而不是你的 squeeze .

以下是我的代码。

#include <type_traits>
#include <utility>

namespace detail
 {
   template <typename Pack>
   struct sequence_traits;

   template <typename T, template <typename, T...> class Z, T... Is>
   struct sequence_traits<Z<T, Is...>>
    { using templ_empty = Z<T>; };

   template <typename T, template <T...> class Z, T... Is>
   struct sequence_traits<Z<Is...>>
    { using templ_empty = Z<>; };
 }

// accumulated

template <typename T, typename, T...>
struct accumulated_h2;

template <typename T, template <typename, T ...> class Q, T ... Ts>
struct accumulated_h2<T, Q<T>, Ts...>
 { using type = Q<T, Ts...>; };

template <typename T, template <T ...> class Q, T ... Ts>
struct accumulated_h2<T, Q<>, Ts...>
 { using type = Q<Ts...>; };

template <typename T, typename, typename, T...>
struct accumulated_h;

template <typename T, typename C, T Sum, T... Is>
struct accumulated_h<T, std::integer_sequence<T, Sum, Is...>, C>
 { using type = typename accumulated_h2<T, C, Is..., Sum>::type; };

template <typename T, T Sum, T... Is, typename C, T Next, T... Rest>
struct accumulated_h<T, std::integer_sequence<T, Sum, Is...>, C, Next,
                     Rest...>
   : accumulated_h<T, std::integer_sequence<T, Sum + Next, Is..., Sum>,
                   C, Rest...>
 { };

template <typename T,
          typename = typename detail::sequence_traits<T>::templ_empty>
struct accumulated;

template <typename T, template <T...> class Z, T First,
          T... Rest, typename C>
struct accumulated<Z<First, Rest...>, C>
   : accumulated_h<T, std::integer_sequence<T, First>, C, Rest...>
 { };

template <typename T, template <typename, T...> class Z, T First,
          T... Rest, typename C>
struct accumulated<Z<T, First, Rest...>, C>
   : accumulated_h<T, std::integer_sequence<T, First>, C, Rest...>
 { };

// Testing

template <int...>
struct Z;

template <int...>
struct Q;

template <std::size_t...>
struct I;

int main ()
 {
   static_assert(std::is_same<
      accumulated<Z<1,2,3,4,5>, Q<>>::type,
      Q<1,3,6,10,15>>::value, "!");
   static_assert(std::is_same<
      accumulated<Z<1,2,3,4,5>>::type,
      Z<1,3,6,10,15>>::value, "!");    
   static_assert(std::is_same<
      accumulated<Z<1,2,3,4,5>, std::integer_sequence<int>>::type,
      std::integer_sequence<int, 1,3,6,10,15>>::value, "!");
   static_assert(std::is_same<
      accumulated<I<1,2,3,4,5>, std::integer_sequence<std::size_t>>::type,
      std::index_sequence<1,3,6,10,15>>::value, "!");
   static_assert(std::is_same<
      accumulated<std::index_sequence<1,2,3,4,5>>::type,
      std::index_sequence<1,3,6,10,15>>::value);
   static_assert(std::is_same<
      accumulated<std::index_sequence<1,2,3,4,5>, I<>>::type,
      I<1,3,6,10,15>>::value);
 }

关于c++ - 模板类型略有变化后继承特征类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46798802/

相关文章:

c++ - 迭代多个模板参数的递归模板函数

c++ - 扩展容器到参数包

c++ - 等同于 MFC 窗口的 OnFinalMessage?

c++ - 64 位机器上的 STL vector 大小

c++ - OMNeT++ TicToc 扩展字符串消息

c++ - C++ 中的模板

templates - Visual Studio 2017的MVC 5模板

c++ - 模板类的运算符<<()

c++ - 如何通过索引从可变模板参数包中提取值?

c++ - 在非标准位置使用 icpc 和 gnu libstdc++