简体   繁体   English

用模板类专门化消除代码冗余的标准方法是什么?

[英]what is the standard way to eliminate code redundancy with template class specialization?

I have a template class which recursively build fibonacci array using template recursion like this: 我有一个模板类,它使用模板递归来递归地构建斐波那契数组,如下所示:

#include <iostream>
#include "C++ Helpers/static_check.hpp"
using namespace std;

template <typename T>
struct valid_type : std::integral_constant<bool, std::is_integral<T>::value  > {};

template <size_t N, typename = void>
struct fibonacci {
    static const int value = fibonacci<N - 1>::value + fibonacci<N - 2>::value;
};
template <size_t N>
struct fibonacci <N, typename std::enable_if<(N < 2)>::type > {
    static const int value = 1;
};
template <size_t N, typename T = int>
struct fibonacci_array {
    static_assert(valid_type<T>::value, "invalid storage type for fibonacci sequence");
// the base case specialization should has all general case code except for the next line
    fibonacci_array<N - 1, T> generate_sequence_here;
    int value;
    fibonacci_array() : value(fibonacci<N - 1>::value) {}
    int operator [] (size_t pos) {
        return *((int*)this + pos);
    }
};

template <typename T>
struct fibonacci_array<1, T> {
    static_assert(valid_type<T>::value, "invalid storage type for fibonacci sequence");
    int value;
    fibonacci_array() : value(fibonacci<0>::value) {}
    int operator [] (size_t pos) {
        return *((int*)this + pos);
    }
};

int main () {
    const size_t n = 10;
    fibonacci_array<n, int> arr;
    for(size_t i = 0; i < n; ++i)
        cout << arr[i] << endl;
    return 0;
}

what i want to do is to eliminate code redundancy in the base case specialization (when N == 1 ) Note: if i partitioned the class members into private and public ones and used direct inheritance, it wouldn't be efficient because the private members are actually get inherited but without access to them. 我想做的是消除基本情况下的代码冗余(当N == 1 )注意:如果我将类成员划分为私有成员和公共成员,并使用直接继承,那么效率将不高,因为私有成员实际上是继承的,但是无法访问它们。 i'm thinking of creating a base class to make the general template class and the specialization inherit from it, but i don't know the exact syntax for that and if there's nicer way it would be better. 我正在考虑创建一个基类,以使通用模板类和专业化类从中继承,但是我不知道确切的语法,如果有更好的方法,它将更好。 thanks! 谢谢!

How about terminating your recursion at N=0 ? 如何在N=0处终止递归呢? In this case you can throw away the whole body for fibonacci_array<1, T> and replace with this little guy: 在这种情况下,您可以扔掉fibonacci_array<1, T>的整个身体fibonacci_array<1, T>然后用这个小家伙代替:

template <typename T>
struct fibonacci_array<0, T> 
{
};

One pitfall is that this specialization will be empty, but when you aggregate it to the main class as generate_sequence_here , it will consume 1 byte of space, since in C++ every object shall have a unique address. 一个陷阱是该专门化将是空的,但是当您将其作为generate_sequence_here聚合到主类时,它将占用1个字节的空间,因为在C ++中,每个对象都应具有唯一的地址。 This will waste you 1 byte of space (and will require to update your operator[] ). 这将浪费您1个字节的空间(并且需要更新您的operator[] )。 Don't worry though, it can be easily solved, if you change aggregation of fibonacci_array<N - 1, T> to inheritance from it. 不过请放心,如果将fibonacci_array<N - 1, T>聚合更改为从其继承,则可以轻松解决。 This works thanks to the feature called "empty base class optimization". 这要归功于称为“空基类优化”的功能。

Also, if you can use C++14, prefer constexpr constructors for this task instead, the code will be much cleaner: 另外,如果可以使用C ++ 14,则最好使用constexpr构造函数代替此任务,代码将更加简洁:

template <int N, typename T = int>
struct fibonacci_array 
{
    int values[N];
    constexpr fibonacci_array() : values()
    {
        if (N > 1)
            values[1] = 1;
        for (int i = 2; i < N; ++i)
            values[i] = values[i - 1] + values[i - 2];
    }
    constexpr int operator [] (size_t pos) const
    {
        return values[pos];
    }
};

See the demo . 参见演示

Also see how compiler was able to compute it on compile time: https://godbolt.org/g/kJEFvN 另请参阅编译器如何在编译时进行计算: https//godbolt.org/g/kJEFvN

Sorry... I'm not a standard expert... but I find your mode to access to the values 抱歉...我不是标准专家...但是我发现您可以访问值的方式

    return *((int*)this + pos);

a little dangerous. 有点危险。

I suggest to avoid the class recursion, to use a simple constexpr function to calculate the values 我建议避免类递归,使用简单的constexpr函数来计算值

template <typename T>
constexpr T fibonacci (T const & val)
 { return val < T{2} ? T{1} : (fibonacci(val-T{1})+fibonacci(val-T{2})); }

and, using a delegating constructor, initialize a std::array in a simple class 然后使用委托的构造函数在一个简单的类中初始化std::array

template <size_t N, typename T = std::size_t>
struct fibonacci_array
 {
   static_assert(std::is_integral<T>::value,
                 "invalid storage type for fibonacci sequence");

   std::array<T, N> values;

   template <T ... Is>
   constexpr fibonacci_array (std::integer_sequence<T, Is...> const &)
      : values{ { fibonacci(Is)... } }
    { }

   constexpr fibonacci_array ()
      : fibonacci_array{std::make_integer_sequence<T, N>{}}
    { }

   T operator [] (size_t pos) const
    { return values[pos]; }

   T & operator [] (size_t pos)
    { return values[pos]; }
 };

This solution use std::integer_sequence and std::make_integer_sequence , so is a C++14 solution; 此解决方案使用std::integer_sequencestd::make_integer_sequence ,因此C ++ 14解决方案也是如此; but if you need a C++11 solution, substitute they isn't really difficult. 但是,如果您需要C ++ 11解决方案,那么替代它们并不是很难。

The full working example 完整的工作示例

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

template <typename T>
constexpr T fibonacci (T const & val)
 { return val < T{2} ? T{1} : (fibonacci(val-T{1})+fibonacci(val-T{2})); }

template <std::size_t N, typename T = std::size_t>
struct fibonacci_array
 {
   static_assert(std::is_integral<T>::value,
                 "invalid storage type for fibonacci sequence");

   std::array<T, N> values;

   template <T ... Is>
   constexpr fibonacci_array (std::integer_sequence<T, Is...> const &)
      : values{ { fibonacci(Is)... } }
    { }

   constexpr fibonacci_array ()
      : fibonacci_array{std::make_integer_sequence<T, N>{}}
    { }

   T operator [] (std::size_t pos) const
    { return values[pos]; }

   T & operator [] (std::size_t pos)
    { return values[pos]; }
 };

int main ()
 {
   constexpr std::size_t n { 10U };

   fibonacci_array<n, int> arr;

   for (auto i = 0U; i < n ; ++i )
      std::cout << arr[i] << std::endl;
 }

-- EDIT -- -编辑-

As pointed by Mikhail, my fibonacci() function could compute with exponenzial complexity. 正如Mikhail指出的那样,我的fibonacci()函数可以以指数复杂度进行计算。

With the same exponential algorithm, but using static constexpr values in structs, so using memoization (I hope), the following code should avoid exponential complexity. 使用相同的指数算法,但在结构中使用静态constexpr值,因此使用记忆(我希望),以下代码应避免指数复杂性。

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

template <typename T, T N, bool = (N < T{2})>
struct fibonacci;

template <typename T, T N>
struct fibonacci<T, N, false>
   : std::integral_constant<T,   fibonacci<T, N-1U>::value
                               + fibonacci<T, N-2U>::value>
 { };

template <typename T, T N>
struct fibonacci<T, N, true>
   : std::integral_constant<T, 1U>
 { };

template <std::size_t N, typename T = std::size_t>
struct fibonacci_array
 {
   static_assert(std::is_integral<T>::value,
                 "invalid storage type for fibonacci sequence");

   std::array<T, N> values;

   template <T ... Is>
   constexpr fibonacci_array (std::integer_sequence<T, Is...> const &)
      : values{ { fibonacci<T, Is>::value... } }
    { }

   constexpr fibonacci_array ()
      : fibonacci_array{std::make_integer_sequence<T, N>{}}
    { }

   T operator [] (std::size_t pos) const
    { return values[pos]; }

   T & operator [] (std::size_t pos)
    { return values[pos]; }
 };

int main ()
 {
   constexpr std::size_t n { 10U };

   constexpr fibonacci_array<n, int> const arr;

   for (auto i = 0U; i < n ; ++i )
      std::cout << arr[i] << std::endl;
 }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM