Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this constexpr static member function not seen as constexpr when called?

Tags:

Why is this constexpr static member function, identified by the //! Nah comment, not seen as constexpr when called?

struct Item_id
{
    enum Enum
    {
        size, position, attributes, window_rect, max_window_size, _
    };

    static constexpr int n_items_ = _;                          // OK
    constexpr auto member_n_items() const -> int { return _; }  // OK
    static constexpr auto static_n_items() -> int { return _; } // OK
    static constexpr int so_far = n_items_;                     // OK
    #ifndef OUT_OF_CLASS
        static constexpr int bah = static_n_items();            //! Nah.
    #endif
};

constexpr auto n_ids() -> int { return Item_id().member_n_items(); }    // OK

auto main() -> int
{
    #ifdef OUT_OF_CLASS
        static constexpr int bah = Item_id::static_n_items();   // OK
    #endif
}

MinGW g++ 5.1 reports

constexpr.cpp:12:46: error: 'static constexpr int Item_id::static_n_items()' called in a constant expression
     static constexpr int bah = static_n_items();                //! Nah.

Visual C++ 2015 reports

constexpr.cpp(12): error C2131: expression did not evaluate to a constant
constexpr.cpp(12): note: failure was caused by call of undefined function or one not declared 'constexpr'
constexpr.cpp(12): note: see usage of 'Item_id::static_n_items'

My text editor insists that the name in the call is the same as the name in the function definition.

It appears to have something to do with incomplete class, because with OUT_OF_CLASS defined it compiles nicely.

But then why does the n_items_ data work, and, why such a rule (doesn't make sense to me)?

like image 453
Cheers and hth. - Alf Avatar asked May 12 '16 17:05

Cheers and hth. - Alf


People also ask

Can a constexpr function call a non constexpr function?

A call to a constexpr function produces the same result as a call to an equivalent non- constexpr function , except that a call to a constexpr function can appear in a constant expression. The main function cannot be declared with the constexpr specifier.

How do I know if a function is constexpr?

The easiest way to check whether a function (e.g., foo ) is constexpr is to assign its return value to a constexpr as below: constexpr auto i = foo(); if the returned value is not constexpr compilation will fail.

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.

Why does constexpr need 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.


2 Answers

From memory, member function bodies are evaluated only once the class has been completely defined.

static constexpr int bah = static_n_items(); 

forms part of the class definition, but it's referring to a (static) member function, which cannot yet be defined.

Solution:

defer constant expressions to a base class and derive from it.

e.g.:

struct Item_id_base
{
    enum Enum
    {
        size, position, attributes, window_rect, max_window_size, _
    };

    static constexpr int n_items_ = _;                          // OK
    constexpr auto member_n_items() const -> int { return _; }  // OK
    static constexpr auto static_n_items() -> int { return _; } // OK
    static constexpr int so_far = n_items_;                     // OK
};

struct Item_id : Item_id_base
{
    #ifndef OUT_OF_CLASS
        static constexpr int bah = static_n_items();            // now OK
    #endif
};

constexpr auto n_ids() -> int { return Item_id().member_n_items(); }    // OK

auto main() -> int
{
    #ifdef OUT_OF_CLASS
        static constexpr int bah = Item_id::static_n_items();   // OK
    #endif
}

Why do you think the standard disallows it?

Because this is illegal:

struct Item_id
{   
    // ... etc.

    #ifndef OUT_OF_CLASS
        static constexpr int bah;// = static_n_items();            //! Nah.
    #endif
};

constexpr int Item_id::bah = static_n_items();

And a constexpr must have a constexpr definition. The only place we can define it is during its declaration...

... so by deduction it cannot refer to any function who's body is not yet defined.

I am at a loss to know where to look in the standard for all that. Probably 5 different, seemingly unrelated clauses :)

like image 59
Richard Hodges Avatar answered Oct 21 '22 04:10

Richard Hodges


[class.mem]/2

Within the class member-specification, the class is regarded as complete within function bodies, default arguments, exception-specifications, and default member initializers (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification.

In the initializer of a static data member of a class, the class is incomplete. The initializer can only see the declarations of members which precede it, and any member functions it can see are considered declared but not defined. A call to a function that is declared but not defined can not be a constant expression.

like image 45
Oktalist Avatar answered Oct 21 '22 05:10

Oktalist