Possible to instantiate templates using a for loop

2019-02-16 07:37发布

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)?

1条回答
\"骚年 ilove
2楼-- · 2019-02-16 08:02

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
}
查看更多
登录 后发表回答