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

时间:2017-05-27 16:36:09

标签: c++ c++11 templates template-meta-programming template-specialization

我有一个模板类,使用模板递归递归构建fibonacci数组,如下所示:

#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时) 注意:如果我将类成员划分为私有和公共成员并使用直接继承,那么它将不会有效,因为私有成员实际上是继承但无法访问它们。 我想创建一个基类来使通用模板类和专业化继承它,但我不知道它的确切语法,如果有更好的方法它会更好。谢谢!

2 个答案:

答案 0 :(得分:3)

如何在N=0终止递归?在这种情况下,你可以扔掉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];
    }
};

请参阅demo

另请参阅编译器如何在编译时计算它:https://godbolt.org/g/kJEFvN

答案 1 :(得分:1)

抱歉......我不是标准专家......但我找到了访问价值的模式

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
</head>
<body>

<div id="fb-root"></div>
<script>(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];

  if (d.getElementById(id)) return;

  js = d.createElement(s); js.id = id;
  js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.9&appId=x";
  fjs.parentNode.insertBefore(js, fjs);
  }
  (document, 'script', 'facebook-jssdk'));
</script>

<div class="fb-login-button" data-max-rows="1" 
   data-size="large" data-button-type="continue_with" data-show-faces="false" 
   data-auto-logout-link="false" data-use-continue-as="false">
</div>

</body>
</html>

有点危险。

我建议避免类递归,使用简单的 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_sequence,因此是C ++ 14解决方案;但是如果你需要一个C ++ 11解决方案,替换它们并不是非常困难。

完整的工作示例

std::make_integer_sequence

- 编辑 -

正如米哈伊尔所指出的,我的#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; } 函数可以用指数复杂度计算。

使用相同的指数算法,但在结构中使用静态constexpr值,因此使用memoization(我希望),以下代码应该避免指数复杂性。

fibonacci()