HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Initializing a std::array from a recursive template computation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
stdtemplateinitializingcomputationarrayrecursivefrom

Problem

I wrote this code for an answer over on SO. I suspect it's possible to clean it up a bit, particularly the base-case specialization for FactorialArray, which is repetitive and contains odd bits (for instance: why double curly braces? because clang complains about un-braced subobject initializers otherwise...)

Any and all suggestions are appreciated. Note that Factorial is separate from FactorialArray for pedagogical clarity; I realize this makes the compile-time calculation O(N2) unless the compiler is clever enough to memoize it.

#include 
#include 
#include 

using std::uint64_t;

// Helper template that computes the factorial of one integer
template struct Factorial
{ static constexpr uint64_t value = I * Factorial::value; };

template<> struct Factorial { static constexpr uint64_t value = 1; };

// FactorialArray recursively assembles the desired array as a variadic
// template argument pack from a series of invocations of Factorial
template struct FactorialArray
  : FactorialArray::value, Values...>
{};

// and in the base case, initializes a std::array with that pack
template struct FactorialArray
  : std::array
{
  constexpr FactorialArray()
    : std::array ({{Values...}})
  {}
};

int main()
{
  static FactorialArray f;
  for (std::size_t i = 0; i < f.size(); i++)
    std::cout << i << "! = " << f[i] << '\n';
  return 0;
}

Solution

I think your code is fine for the most part. I don't like the inheritance from std::array, seeing as you can do the same thing with a static array data member, which would make the code easier to maintain (less coupling).

If you're well-and-truly in C++14 land, I think the code is clearer if you use constexpr functions rather than functions-disguised-as-structs:

#include 
#include 
#include 

constexpr size_t factorial(size_t t) {
    return t == 0 ? 1 : t * factorial(t - 1);
}

template
constexpr auto factorial_array(std::index_sequence) {
    return std::array{{factorial(i)...}};
}

template
constexpr auto factorial_array() {
    return factorial_array(std::make_index_sequence{});
}

int main() {
  constexpr auto f = factorial_array();
  for (size_t i = 0; i < f.size(); i++) {
    std::cout << i << "! = " << f[i] << '\n';
  }
  return 0;
}

Code Snippets

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

constexpr size_t factorial(size_t t) {
    return t == 0 ? 1 : t * factorial(t - 1);
}

template<size_t... i>
constexpr auto factorial_array(std::index_sequence<i...>) {
    return std::array<size_t, sizeof...(i)>{{factorial(i)...}};
}

template<size_t size>
constexpr auto factorial_array() {
    return factorial_array(std::make_index_sequence<size>{});
}

int main() {
  constexpr auto f = factorial_array<15>();
  for (size_t i = 0; i < f.size(); i++) {
    std::cout << i << "! = " << f[i] << '\n';
  }
  return 0;
}

Context

StackExchange Code Review Q#135631, answer score: 7

Revisions (0)

No revisions yet.