When I declare a template class, I'm able to use any non-type template arguments in constexpr member functions, such as this:
template <int Value>
struct Foo {
constexpr int value() const { return Value; }
};
I can then use this function later to initialize constexpr variables like so:
template <int Value>
void use_value(Foo<Value> foo) {
constexpr int baz{ foo.value() };
}
int main() {
Foo<10> bar;
use_value(bar);
}
What I don't understand is that when I take foo
by reference...
template <int Value>
void use_value(Foo<Value>& foo) { // by ref
constexpr int baz{ foo.value() };
}
int main() {
Foo<10> bar;
use_value(bar);
}
...clang will tell me that foo.value()
is not a constant expression!
test.cpp:8:24: error: constexpr variable 'baz' must be initialized by a constant expression
constexpr int baz{ foo.value() };
~~^~~~~~~~~~~~~
test.cpp:13:5: note: in instantiation of function template specialization 'use_value<10>'
requested here
use_value(bar);
Why does this happen? Has it got something to do with fact that in the first example the compiler was able to follow the entire lifetime of foo
, whereas when taken by reference, some other part of the program could alter the instance, somehow affecting the constexpr-ness of foo.value()
? Although it's clearly not the case here (given the template specialization, there is no way of changing Value
's value), I'm not sure if you could indeed "ruin" constexpr member functions by tampering with the instance in the middle of its execution in some cases.
Edit
Okay, so references are bad for constexpr, since they have a runtime location - and constexpr values are hardcoded in the resulting executable. How about taking a constant reference? This works:
int main() {
constexpr int foo2{5};
int const& bar2{ foo2 };
}
But with the Foo
class, it won't help to change the reference to const
:
template <int Value>
void use_value(Foo<Value> const& foo) { // const ref
constexpr int baz = foo.value();
}
I get the same compiler error. What is the difference here?