Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possible to instantiate templates using a for loop in a C++14 constexpr function?

I've been messing around with an SVN build of clang to experiment with the relaxed rules for constexpr. One of the things I've been unable to determine as of yet is whether it's possible to loop through the elements inside a tuple at compile-time in a constexpr function.

Because I don't have a C++14 compliant standard library to test on, I've prepared the following equivalent test:

template<int N>
constexpr int foo() {
  return N;
}

constexpr int getSum() {
  auto sum = 0;
  for (auto i = 0; i < 10; ++i) {
    sum += foo<i>();
  }
  return sum;
}

constexpr auto sum = getSum();

The interesting part here is foo<i>(). In a non-constexpr function I would expect this to fail to compile, because you simply cannot use a runtime int to generate a compile-time instantiation of a template. Because this is a constexpr function, however, I question whether this would be possible. In particular, the value is known at compile-time, even if it is allowed to mutate.

I know that the following code will compile:

constexpr auto nValue = 2;
foo<nValue>();

In SVN clang, my first example does not:

test2.cpp:19:12: error: no matching function for call to 'foo'
    sum += foo();
           ^~~~~~
test2.cpp:11:15: note: candidate template ignored: invalid explicitly-specified
      argument for template parameter 'N'
constexpr int foo() {
              ^

For starters, I struggle to interpret the second part of this error message. That aside, is it mandated by the C++14 standard, and if so, does anyone know why this syntax wouldn't be allowed (simple oversight or to protect against something)?

like image 645
Mark Avatar asked Dec 07 '13 01:12

Mark


People also ask

What is constexpr function in C++?

A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument.

Can a constexpr function throw?

Even though try blocks and inline assembly are allowed in constexpr functions, throwing exceptions or executing the assembly is still disallowed in a constant expression.

Is constexpr static?

constexpr int a = 2; Static specifies the lifetime of the variable. A static constexpr variable has to be set at compilation, because its lifetime is the the whole program. Without the static keyword, the compiler isn't bound to set the value at compilation, and could decide to set it later.

Where are constexpr stored?

For example, constexpr variables and instances of user-defined types are automatically thread-safe and can be stored in ROM; constexpr functions that are evaluated at compile-time, are done with their work at run time.


1 Answers

That aside, is it mandated by the C++14 standard, and if so, does anyone know why this syntax wouldn't be allowed (simple oversight or to protect against something)?

That's because constexpr is not exclusive to compile-time computations or usage. A constexpr function is just that, allowing a function (or variable) to be used in a constant expression. Outside of that, they're regular functions. A constant expression just so happens to be required in certain contexts such as static_assert or array sizes, etc that are compile-time only situations.

You'll notice in your code that you loop through a variable but the variable you're looping through itself isn't constexpr so it isn't a constant expression to be used in that template instantiation of N. As it stands, it's no different than doing this in C++11:

constexpr bool f(int x) {
    static_assert(x > 10, "..."); // invalid
    return true;
}

Which is obviously invalid because as I mentioned earlier, you don't have to use constexpr functions in exclusive compile-time situations. For example, nothing is stopping you from doing this:

constexpr int times_ten(int x) {
    return x * 10;
}

int main() {
   int a = times_ten(20); // notice, not constexpr
   static_assert(times_ten(20) == 200, "...");
   static_assert(a == 200, "..."); // doesn't compile
}
like image 194
Rapptz Avatar answered Oct 10 '22 22:10

Rapptz