I came up with this class:
class Point
{
public:
int X, Y;
mutable int Z;
constexpr Point(int x, int y) :X (x), Y(y), Z(0)
{ }
constexpr int GetX() const
{
// Z++; // Wont compile, but following expression is valid!
return X+Z;
}
int GetY() const
{
Z++;
return Y;
}
void FoolConst() const
{
Z++;
}
};
And here is usage:
template<int S>
void foo()
{
std::cout << S << std::endl;
}
int main()
{
constexpr Point pt(10, 20);
pt.FoolConst();
char arr[pt.GetX()]; // Both compile, but GCC is using extended `new`
foo<pt.GetX()>(); // GCC fails, VC compiles
std::cout << sizeof(arr); // 10 (MSVC), 11 (GCC)
std::cout << pt.GetX(); // 11 (MSVC), 11(GCC)
}
Questions:
GetX
is compiling well with X+Z
as return expression (Z is not constexpr).FoolConst
and GetY
methods out of constexpr
object (pt
) ? GetX
in main
is different in compilers. MSVC compiles fine with a int
as template argument, while GCC (IdeOne) won't compile it.For one compiler constexpr GetX
is truly constexpr
, but for other it is not if X+Z
is involved. If I remove +Z
and simply return X
GCC is okay.
My question is very basic: If object is constexpr
how can it call a non-constexpr method?
const can only be used with non-static member functions whereas constexpr can be used with member and non-member functions, even with constructors but with condition that argument and return type must be of literal types.
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?
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.
The primary difference between const and constexpr variables is that the initialization of a const variable can be deferred until run time. A constexpr variable must be initialized at compile time.
A constant expression cannot access a mutable sub-object. This is in [expr.const]/2:
A conditional-expression
e
is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions: [...]
- an lvalue-to-rvalue conversion (4.1) unless it is applied to [...]
- a non-volatile glvalue that refers to a non-volatile object defined with constexpr, or that refers to a non-mutable sub-object of such an object [...]
So GetX
cannot be used within a constant expression, e.g. as a template parameter foo<pt.GetX()>()
.
In answer to your specific questions:
- Why GetX is compiling well with X+Y as return expression (Z is not constexpr).
A compiler is not required to check that constexpr functions (incl. member functions) are fully valid when they are defined, only when they are used. It does have to check a few things, like not using goto
[dcl.constexpr]/3, but it doesn't have to check which objects the definition accesses. This is because whether the constexpr function can be used within a constant expression can depend on the values of its arguments.
In fact, because GetX
unconditionally accesses Z
, your program strictly has undefined behavior per [dcl.constexpr]/5:
For a non-template, non-defaulted constexpr function or a non-template, non-defaulted, non-inheriting constexpr constructor, if no argument values exist such that an invocation of the function or constructor could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed; no diagnostic required.
"Ill-formed; no diagnostic required" is another way of saying that the behavior of your program is undefined.
- How can I call FoolConst and GetY methods out of constexpr object (pt) ?
That's absolutely fine; an object declared constexpr
is just a const
object from the point of view of non-constexpr
member functions of that object.
- The behaviour of GetX in main is different in compilers. MSVC compiles fine with a int as template argument, while GCC (IdeOne) won't compile it.
Unfortunately, both compilers are correct; your program has undefined behavior in the definition of GetX
, so there is no single correct behavior for the compiler.
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