Say I have a class that that can return a constant expression through a constexpr
function:
template<int N>
struct Foo {
constexpr int Bar() const { return N; }
};
If I wanted to initialize constexpr values from Foo::Bar()
, how should I pass a parameter of type Foo
? I've tried these two, with an example constexpr
variable inside of each to test that it can be initialized:
template<int N>
constexpr int ByValue(Foo<N> f) {
constexpr int i = f.Bar();
return f.Bar();
}
template<int N>
constexpr int ByReference(const Foo<N> &f) {
constexpr int i = f.Bar();
return f.Bar();
}
constexpr int a = ByValue(Foo<1>{});
constexpr int b = ByReference(Foo<1>{});
But clang 3.7 raises an error on ByReference
while gcc >=5.1 does not: Live demo
main.cpp:15:25: error: constexpr variable 'i' must be initialized by a constant expression
constexpr int i = f.Bar();
^~~~~~~
main.cpp:22:25: note: in instantiation of function template specialization 'ByReference<1>' requested here
constexpr int b = ByReference(Foo<1>{});
What's the difference between taking a const Foo &
or a plain Foo
, when Bar
is constexpr
either way and returns a valid constant expression?
Which is right and why, GCC or Clang? If available, references to the standard would be appreciated.
#define (also called a 'macro') is simply a text substitution that happens during preprocessor phase, before the actual compiler. And it is obviously not typed. constexpr on the other hand, happens during actual parsing. And it is indeed typed.
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. So, what does constexpr mean?
To be constexpr, a function must be rather simple: just a return-statement computing a value. A constexpr function can be used for non-constant arguments, but when that is done the result is not a constant expression.
No. Absolutely not. Not even close. Apart from the fact your macro is an int and your constexpr unsigned is an unsigned , there are important differences and macros only have one advantage.
§5.20:
The reference does not have a preceding initialization from the point of view of i
, though: It's a parameter. It's initialized once ByReference
is called.
Let's remove the constexpr
from i
's declaration and consider an invocation of ByReference
in its entirety:
template<int N>
constexpr int ByReference(const Foo<N> &f) {
int i = f.Bar();
return i;
}
constexpr int j = ByReference(Foo<0>());
This is fine, since f
does have preceding initialization. The initializer of f
is a constant expression as well, since the implicitly declared default constructor is constexpr
in this case (§12.1/5).
Hence i
is initialized by a constant expression and the invocation is a constant expression itself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With