Koothrappali
Koothrappali

Reputation: 157

use a function parameter as template parameter

template<unsigned int size>
struct Vec {
    Vec() = default;
};

auto build_vec(unsigned int size) {
    return Vec<size>();
}

int main() {
    auto vec = build_vec(5);
    return 0;
}

This program doesn't compile as non-type template argument is not a constant expression. Basically, the size parameter sent into build_vec is not known to the compiler during compilation type.

Then I wonder, can I add some keyword to force size to be evaluated during compile time thus struct Vec can be built through a function?

Upvotes: 0

Views: 184

Answers (3)

nhatnq
nhatnq

Reputation: 1193

It does NOT fit your expectation completely but rely on boost hana approach we may have similar one. Below is an example using variable template

#include <iostream>

template<unsigned int size>
struct Vec {
    Vec() = default;
    ~Vec() {std::cout << size << '\n';}
};


template <unsigned int i>
constexpr std::integral_constant<unsigned int, i> uint_c{};

template <unsigned int i>
auto build_vec(std::integral_constant<unsigned int, i>) {
    return Vec<i>();
}

int main() {
    const size_t n = 5;
    auto vec = build_vec(uint_c<n>);
    return 0;
}

Upvotes: 1

Aykhan Hagverdili
Aykhan Hagverdili

Reputation: 29975

my code base constrains me that it has to be a function parameter, not template parameter.

That's not possible. Return type of a function cannot be dependent on runtime values. Your options are:

  1. Make the template argument of Vec a runtime value as well.
  2. If the possible set of runtime values are small, you may generate all the code needed at compile time and use a few runtime branches to determine what to use. The return type cannot depend on this, so you can return a type-erased object (std::any, etc), have the Vec class derive from some base class, or not return the object. Don't do this if the range is too large. It may generate lots of code-bloat:
#include <cstdio>
#include <utility>

template <unsigned size>
struct Vec {
  Vec() = default;
  void use_value() { std::printf("My constexpr value is %u\n", size); }
};

template <unsigned ct_val>
void build_vec_impl_impl() {
  Vec<ct_val> cant_return;
  cant_return.use_value();
}

template <unsigned... vals>
void build_vec_impl(std::integer_sequence<unsigned, vals...>, unsigned rt_val) {
  using fp_builder = void (*)();

  constexpr fp_builder builders[]{build_vec_impl_impl<vals>...};
  builders[rt_val]();
}

template <unsigned max>
void build_vec(unsigned const rt_val) {
  using seq = std::make_integer_sequence<unsigned, max + 1>;
  build_vec_impl(seq{}, rt_val);
}

int main() {
  auto constexpr max = 10;
  for (unsigned i = 0; i < max; ++i) build_vec<max>(i);
}

   3. Use a dynamic language like Python where return type may depend on runtime values

Upvotes: 0

463035818_is_not_an_ai
463035818_is_not_an_ai

Reputation: 122458

No, this is not possible. A functions parameter is not known at compile time. The common way to signal that size must be known at compile time is to make the function a template and size a template parameter:

template<unsigned int size>
struct Vec {
    Vec() = default;
};

template <unsigned int size>
auto build_vec() {
    return Vec<size>();
}

int main() {
    auto vec = build_vec<5>();
}

Though, now it should be obvious that the function does not help to turn a runtime value into a compile time value. In general thats not possible, unless you implement such mapping manually. You don't need build_vec because the caller can use auto vec = Vec<size>(); directly.

Upvotes: 1

Related Questions