Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr object with mutable member

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:

  • Why GetX is compiling well with X+Z as return expression (Z is not constexpr).
  • How can I call FoolConst and GetY methods out of constexpr object (pt) ?
  • 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.

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?

like image 653
Ajay Avatar asked Aug 09 '16 12:08

Ajay


People also ask

Can a member function be constexpr?

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.

Does constexpr have to be static?

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?

Is constexpr can be used with #define macros?

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.

Should I use constexpr or const?

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.


1 Answers

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.

like image 119
ecatmur Avatar answered Oct 23 '22 03:10

ecatmur