26

I have a constexpr function taking a single argument, and I'm calling the function with a constant. However, it seems I can't use that argument as a constant expression:

constexpr void f(const int i) 
{       
  constexpr int m = i;       // error (i is not a constant expression)
  static_assert(i == 42);    // error (i is not a constant expression)
  std::array<int, i> arr{};  // error (i is not a constant expression)
}
    
int main()
{
  f(42);
}

How can i be used as a constant expression?

12
  • 1
    Well because in the general case, i isn't constexpr in void t1(const int). Commented May 9, 2014 at 16:15
  • 1
    how can I make it constexpr, then? Commented May 9, 2014 at 16:21
  • making it a template argument is your only option Commented May 9, 2014 at 16:22
  • 6
    There is no direct way to do what you want to do. This feature/limitation is probably the most frequently asked question about constexpr. Commented May 9, 2014 at 21:06
  • 3
    Possible duplicate of Why is comparing two parameters of a constexpr function not a constant condition for static assertion? Commented Jul 23, 2019 at 16:20

3 Answers 3

35

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);
Sign up to request clarification or add additional context in comments.

Comments

12

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.

Comments

0

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.

5 Comments

Making t1 constexpr wouldn't change anything.
Yes, I test your suggestion. Same old story.
@MarcGlisse t1() is not correctly formed to be a constexpr function, I wasn't suggesting making it so would correct the problem.
@praxos1977 - see updated question. Your suggestion is wrong.
@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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.