2

I fully expect this to not be a feature, but figured I may as well ask; is it possible to expand code at compile time using template parameters?

For example:

template <size I>
void foo()
{
    ...double... vec;
}

Where the ... Is replaced by std::vector< >, I times.

So foo<2>() would compile to:

void foo()
{
   std::vector<std::vector<double>> vec;
}

I can't even imagine what the syntax for this would be, so I'm not hopeful.

It would be useful for something like an N dimensional binning class, which could also be implemented through recursion, but not so neatly imo.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
user7119460
  • 1,451
  • 10
  • 20

2 Answers2

3

Yes, you can. You can do it with class templates and specializations, like this:

template<std::size_t N>
struct MultiDim {
    using underlying = typename MultiDim<N-1>::type;
    using type = std::vector<underlying>;
};
template<>
struct MultiDim<1> {
    using type = std::vector<double>;
};
template<std::size_t N>
using multi_dimensional = typename MultiDim<N>::type;

Hence, multi_dimensional<1> is vector<double>, and multi_dimensional<N> where N>1 is vector<multi_dimensional<N-1>>.

L. F.
  • 19,445
  • 8
  • 48
  • 82
  • 1
    A little doubt: what happens calling `MultiDim<0>`? (in case of `foo<0>()`)? – max66 Jun 09 '18 at 23:11
  • 2
    That depends on what you like. If you regard 0-dim as a scalar, you can define a `MultiDim<0>` with `using type = double;` in it. If you think this is erroneous, you can add `static_assert(N>0, "some message");` in the template `MultiDim`. – L. F. Jun 09 '18 at 23:23
  • It seems to me that If `foo<0>()` is a valid call (I suppose with `double vec;`), the `static_assert()` solution is wrong, because give an error in a legit implementation, and only a `MultiDim<0>` specialization is the only valid solution. If `foo<0>()` isn't a valid call, both solutions (IMHO) are acceptable. – max66 Jun 10 '18 at 01:43
0

is it possible to expand code at compile time using template parameters?

Directly... I don't think so.

But I suppose you can implement a specific type traits as follows

template <typename T, std::size_t I>
struct bar
 { using type = std::vector<typename bar<T, I-1U>::type>; };

template <typename T>
struct bar<T, 0U>
 { using type = T; };

and use in foo() in this way

template <std::size_t I>
void foo ()
 {
   typename bar<double, I>::type  vec;
 }

If you want to be a little more generic, you can also pass std::vector as a template-template parameter; if you define bar as follows

template <template <typename...> class C, typename T, std::size_t I>
struct bar
 { using type = C<typename bar<C, T, I-1U>::type>; };

template <template <typename ...> class C, typename T>
struct bar<C, T, 0U>
 { using type = T; };

foo() become

template <std::size_t I>
void foo ()
 {
   typename bar<std::vector, double, I>::type  vec;
 }
max66
  • 65,235
  • 10
  • 71
  • 111