簡體   English   中英

用模板類專門化消除代碼冗余的標准方法是什么?

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

我有一個模板類,它使用模板遞歸來遞歸地構建斐波那契數組,如下所示:

#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;
}

我想做的是消除基本情況下的代碼冗余(當N == 1 )注意:如果我將類成員划分為私有成員和公共成員,並使用直接繼承,那么效率將不高,因為私有成員實際上是繼承的,但是無法訪問它們。 我正在考慮創建一個基類,以使通用模板類和專業化類從中繼承,但是我不知道確切的語法,如果有更好的方法,它將更好。 謝謝!

如何在N=0處終止遞歸呢? 在這種情況下,您可以扔掉fibonacci_array<1, T>的整個身體fibonacci_array<1, T>然后用這個小家伙代替:

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

一個陷阱是該專門化將是空的,但是當您將其作為generate_sequence_here聚合到主類時,它將占用1個字節的空間,因為在C ++中,每個對象都應具有唯一的地址。 這將浪費您1個字節的空間(並且需要更新您的operator[] )。 不過請放心,如果將fibonacci_array<N - 1, T>聚合更改為從其繼承,則可以輕松解決。 這要歸功於稱為“空基類優化”的功能。

另外,如果可以使用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];
    }
};

參見演示

另請參閱編譯器如何在編譯時進行計算: https//godbolt.org/g/kJEFvN

抱歉...我不是標准專家...但是我發現您可以訪問值的方式

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

有點危險。

我建議避免類遞歸,使用簡單的constexpr函數來計算值

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

然后使用委托的構造函數在一個簡單的類中初始化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]; }
 };

此解決方案使用std::integer_sequencestd::make_integer_sequence ,因此C ++ 14解決方案也是如此; 但是,如果您需要C ++ 11解決方案,那么替代它們並不是很難。

完整的工作示例

#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;
 }

-編輯-

正如Mikhail指出的那樣,我的fibonacci()函數可以以指數復雜度進行計算。

使用相同的指數算法,但在結構中使用靜態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