C++11 constexpr function pass parameter
Solution 1
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 constexpr
ness 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);
Solution 2
One important difference between const
and constexpr
is that a constexpr
can be evaluated at compile time.
By writing constexpr int ii = make_const(i);
you are telling the compiler that the expression is to be evaluted at compile time. Since i
is evaluted at run-time, the compiler is unable to do this and gives you an error.
Solution 3
Because t1() is not a constexpr function, the parameter i is a runtime variable... which you can't pass to a constexpr function. Constexpr expects the parameter to be known at compile time.
Related videos on Youtube
tower120
Updated on December 07, 2021Comments
-
tower120 over 2 years
Consider the following code:
static constexpr int make_const(const int i){ return i; } void t1(const int i) { constexpr int ii = make_const(i); // error occurs here (i is not a constant expression) std::cout<<ii; } int main() { t1(12); }
Why I have an error on make_const call?
UPDATE
But this one works:
constexpr int t1(const int i) { return make_const(i); }
However, this not:
template<int i> constexpr bool do_something(){ return i; } constexpr int t1(const int i) { return do_something<make_const(i)>(); // error occurs here (i is not a constant expression) }
-
stefan almost 10 yearsWell because in the general case,
i
isn'tconstexpr
invoid t1(const int)
. -
tower120 almost 10 yearshow can I make it constexpr, then?
-
stefan almost 10 yearsmaking it a template argument is your only option
-
Marc Glisse almost 10 yearsYour update does compile (though it does something completely different), what do you mean it doesn't work? (the
const
is useless) -
tower120 almost 10 years@Marc Glisse - Indeed... Question updated.
-
tower120 almost 10 yearsI want to forward that "12" to "do_something" template argument.
-
Lightness Races in Orbit almost 10 yearsMust we guess at the error?
-
tower120 almost 10 years@Lightness Races in Orbit - updapted
-
Marc Glisse almost 10 yearsThere is no direct way to do what you want to do. This feature/limitation is probably the most frequently asked question about constexpr.
-
Peter Cordes over 7 yearsrelated: using
static_assert
when function args are constexpr after inlining, which is what I was looking for when google took me to this question. -
Aconcagua almost 5 years
-
-
Marc Glisse almost 10 yearsMaking t1 constexpr wouldn't change anything.
-
tower120 almost 10 yearsYes, I test your suggestion. Same old story.
-
oz10 almost 10 years@MarcGlisse t1() is not correctly formed to be a constexpr function, I wasn't suggesting making it so would correct the problem.
-
tower120 almost 10 years@praxos1977 - see updated question. Your suggestion is wrong.
-
oz10 almost 10 years@tower120, I wasn't making a suggestion. I was pointing out that you can't pass a runtime variable to a constexpr function. TAS obviously worded his answer better than I did.