C++11 Constexpr Function Pass Parameter

C++11 constexpr function pass parameter

A constexpr function and a constexpr variable are related, but different things.

A constexpr variable is a variable whose value is guaranteed to be available at compile time.

A constexpr function is a function that, if evaluated with constexpr arguments, and behaves "properly" during its execution, will be evaluated at compile time.

If you pass a non-constexpr int to a constexpr function, it will not magically make it evaluated at compile time. It will, however, be allowed to pass the constexprness of its input parameters through itself (normal functions cannot do this).

constexpr on functions is a mixture of documentation and restriction on how they are written and instructions to the compiler.

The reason behind this is to allow the same function to be evaluated both at compile time, and at run time. If passed runtime arguments, it is a runtime function. If passed constexpr arguments, it may be evaluated at compile time (and will be if used in certain contexts).

Note that consteval may be what you are looking for for a function. But maybe not.

You are getting errors because by passing in runtime values, you cannot get a compile time value out.

There are ways around this. My favorite is a std::variant of std::integer_constant; you can pick which is active at runtime, then std::visit to get the compile time constant. The downside is that this can generate a lot of code really easily.

template<auto I>
using constant_t=std::integral_constant<decltype(I),I>;
template<auto I>
constexpr constant_t<I> constant_v={};
template<auto...Is>
using var_enum_t=std::variant<constant_t<Is>...>;
template<class Indexes>
struct var_enum_over;
template<class Indexes>
using var_enum_over_t=typename var_enum_over<Indexes>::type;
template<class T,T...ts>
struct var_enum_over<std::integral_sequence<T,Is...>>{
using type=var_enum_t<Is...>;
};
template<std::size_t N>
using var_index_t=var_enum_over_t<std::make_index_sequence<N>>;

template<std::size_t N>
var_index_t<N> var_index(std::size_t I){
constexpr auto table=[]<std::size_t...Is>(std::index_sequence<Is...>)->std::array<N,var_index_t<N>>{
return { var_index_t<N>(constant_v<Is>)..., };
}(std::make_index_sequence<N>{});
if (I>=N) throw 0; // todo: something better
return table[I];
}

(Probably has typos).

Now you can:

auto idx=var_index<5>(3/* 3 can be runtime */);
std::visit([](auto three){
// three is a compile time value here
}, idx);

How to pass a constexpr as a function parameter c++

You cannot use a function parameter where a compile-expression is expected, because the parameter is not constexpr, even in constexpr function (your constexpr function could also be called with non-constexpr values).

In your case, the easiest solution is probably to use a non-type template parameter:

template <int n_steps>
auto create_step_vectors(double step_size)
{
std::array<double, n_steps + 1> arr;
for (int i = 0; i <= n_steps; i++)
{
arr[i] = i * step_size;
}
return arr;
}

And then

constexpr int n_step{ static_cast<int>(1 / x_step) };
const auto arr = create_step_vectors<n_step>(1.);

Passing arguments to constexpr functions

Basically, constexpr functions can be executed at compile time or run time depending on the context. It is guaranteed to execute at compile time only if all its parameters are constexpr and its result is used in a context requiring constexpr (e.g. assignment to constexpr value, template parameter, or, say, c-style array size). Otherwise, it is evaluated at runtime as any other function. So the p3 and p4 lines are executed at runtime, whereas p5 gives an error because scale(p1) is not constexpr, and actually p6 should also give you an error unless you add a constexpr to the definition of p2. See an example here.

constexpr function parameters as template arguments

You tell the compiler, that addFunc would be a constexpr. But it depents on parameters, that are not constexpr itself, so the compiler already chokes on that. Marking them const only means you are not going to modify them in the function body, and the specific calls you make to the function are not considered at this point.

There is a way you can make the compiler understand you are only going to pass compile time constants to addFunc: Make the parameters a template parameters itself:

template <int x, int y>
constexpr int addFunc() {
return add<x,y>::ret;
}

Then call as

cout << addFunc<1,2>() << endl;

Can I use const & as parameter of a constexpr function?

C++ standard section § 7.1.5 [dcl.constexpr]

The definition of a constexpr function shall satisfy the following constraints:

— it shall not be virtual (10.3);

— its return type shall be a literal type;

— each of its parameter types shall be a literal type;

And section § 3.9 [basic.types]

A type is a literal type if it is:

— void; or

— a scalar type; or

a reference type; or

— an array of literal type; or

— a class type (Clause 9) that has all of the following properties:

— it has a trivial destructor,

— it is an aggregate type (8.5.1) or has at least one constexpr constructor or constructor template
that is not a copy or move constructor, and

— all of its non-static data members and base classes are of non-volatile literal types.

So yes, you can pass parameters by reference to constexpr functions.

Now whether or not your function calls will actually be evaluated at compile time depends on the body and calls of LinInterp01.

Passing parameter pack in constexpr

Function arguments are not constexpr expressions (for good reasons) even if part of constexpr or consteval functions.

If you are willing to make Test::size static, independent of objects:

#include <iostream>

template<class T>
class Test
{
public:
T value;
constexpr static size_t size() { return sizeof(T) + 3; }
};

template<typename ...T>
constexpr size_t calc_types()
{
return (T::size() + ...);
}

template<typename ...T>
constexpr size_t calc_vals(const T&...)
{
return calc_types<T...>();
}

template<typename ...T>
constexpr void wrapper_types()
{
static_assert(calc_types<T...>() <= 11, "oops");
}

template<typename ...T>
constexpr void wrapper_vals(const T&...)
{
wrapper_types<T...>();
}

int main()
{
Test<int> a;
Test<char> b;
// a.size() + b.size() == 11

// works
constexpr int v = calc_vals(a, b);
static_assert(v <= 11, "oops");

// wrapper function
wrapper_vals(a, b);
}


Related Topics



Leave a reply



Submit