Static Member Access in Constant Expressions

Static member access in constant expressions

Clang seems to be in the right. When accessing a static member with the member access syntax [class.static/1]:

A static member s of class X may be referred to using the qualified-id
expression X​::​s; it is not necessary to use the class member access
syntax to refer to a static member. A static member may be referred to
using the class member access syntax, in which case the object
expression is evaluated.

So s.v() will cause s to be evaluated. Now, according to [expr.const/2.11], s is not a constant expression:

2 An expression e is a core constant expression unless the evaluation
of e, following the rules of the abstract machine, would evaluate one
of the following expressions:

[...]

an id-expression that refers to a variable or data member of reference
type unless the reference has a preceding initialization and either:

(2.11.1) - it is initialized with a constant expression or

(2.11.2) - its lifetime began within the evaluation of e;

s doesn't have a preceding initialization with a constant expression, not in the scope of foo.


If you want to access the static members based of a function parameter, without hard-coding the type, the way forward is std::remove_reference_t<decltype(s)>. This is accepted by Clang and GCC both:

#include <type_traits>

struct S
{
constexpr static auto s_v = 42;
constexpr static auto v() { return s_v; }
};

constexpr auto foo(S const& s)
{
constexpr auto v = std::remove_reference_t<decltype(s)>::v();
return v;
}

constexpr auto bar(S const& s)
{
constexpr auto v = std::remove_reference_t<decltype(s)>::s_v;
return v;
}

int main() {}

Accessing a static constexpr member from a member variable, GCC bug?

GCC is correct. A template argument must be a constant expression, and a.n implicitly means this->a.n since a is a member of the enclosing class. But a constant expression evaluation cannot access this inside a non-constexpr member function ([expr.const]/2.1). And even though evaluating this appears unnecessary in order to obtain the value of the static member n, the standard requires that a (which means this->a) be evaluated even though its value is not needed; see [expr.ref]/1 and its footnote.

GCC will accept your code if func is marked constexpr. As pointed out in the comments, it's better to just write A::n, though.

How to access the address of a static const member of a class?

Put

const int A::a;

In the source file, otherwise the compiler doesn't generate an address for a. Note the value is not repeated here.

Constexpr static member function usage

Looks like a bug to me.

The type and meaning of the expression h.size() is defined by [expr.ref] "Class member access":

[expr.post]/3


Abbreviating postfix-expression.id-expression as E1.E2, E1 is called the object expression. [...]

and

[expr.post]/6.3.1


If E2 is a (possibly overloaded) member function, function overload resolution is used to determine whether E1.E2 refers to a static or a non-static member function.

  • (6.3.1) If it refers to a static member function and the type of E2 is “function of parameter-type-list returning T”, then E1.E2 is an lvalue; the expression designates the static member function. The type of E1.E2 is the same type as that of E2, namely “function of parameter-type-list returning T”.

This means h.size has the same type as ::MyClass::size and is evaluated as such, regardless of the fact that h is constexpr or not.

h.size() is then a call to a constexpr function and is a core constant expression according to [expr.const]/4.

Cannot access static member variable from class in array

Unfortunately, you can't refer to another variable or class in the initial declaration of a class property. It's just a limitation of the language as it stands. The general workaround is to initialise the property in the constructor, e.g.

class Bar
{
public $someArray = array();

public function __construct()
{
$this->someArray = array(
Foo::$staticVar
);
}
}

On a vaguely related note, PHP 5.6 did at least make some vague headway in allowing you to define constants as basic expressions, see https://3v4l.org/6TDZV

Constant expression initializer for static class member of type double

In C++03 we were only allowed to provide an in class initializer for static member variables of const integral of enumeration types, in C++11 we could initialize a static member of literal type in class using constexpr. This restriction was kept in C++11 for const variables mainly for compatibility with C++03. We can see this from closed issue 1826: const floating-point in constant expressions which says:

A const integer initialized with a constant can be used in constant expressions, but a const floating point variable initialized with a constant cannot. This was intentional, to be compatible with C++03 while encouraging the consistent use of constexpr. Some people have found this distinction to be surprising, however.

CWG ended up closing this request as not a defect(NAD), basically saying:

that programmers desiring floating point values to participate in constant expressions should use constexpr instead of const.

For reference N1804 the closest draft standard to C++03 publicly available in section 9.4.2 [class.static.data] says:

If a static data member is of const integral or const enumeration type, its declaration in the class definition can
specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear
in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and
the namespace scope definition shall not contain an initializer.

and the draft C++11 standard section 9.4.2 [class.static.data] says:

If a non-volatile const static data member is of integral or enumeration type, its declaration in the class
definition can specify a brace-or-equal-initializer in which every initializer-clause that is an assignment expression
is a constant expression (5.19). A static data member of literal type can be declared in the
class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer
in which every initializer-clause that is an assignment-expression is a constant expression. [...]

this is pretty much the same in the draft C++14 standard.

constexpr initializing static member using static function

The Standard requires (section 9.4.2):

A static data member of literal type can be declared in the class definition with the constexpr specifier; if so, its declaration shall specify a brace-or-equal-initializer in which every initializer-clause that is an assignment-expression is a constant expression.

In your "second attempt" and the code in Ilya's answer, the declaration doesn't have a brace-or-equal-initializer.

Your first code is correct. It's unfortunate that gcc 4.6 isn't accepting it, and I don't know anywhere to conveniently try 4.7.x (e.g. ideone.com is still stuck on gcc 4.5).

This isn't possible, because unfortunately the Standard precludes initializing a static constexpr data member in any context where the class is complete. The special rule for brace-or-equal-initializers in 9.2p2 only applies to non-static data members, but this one is static.

The most likely reason for this is that constexpr variables have to be available as compile-time constant expressions from inside the bodies of member functions, so the variable initializers are completely defined before the function bodies -- which means the function is still incomplete (undefined) in the context of the initializer, and then this rule kicks in, making the expression not be a constant expression:

an invocation of an undefined constexpr function or an undefined constexpr constructor outside the definition of a constexpr function or a constexpr constructor;

Consider:

class C1
{
constexpr static int foo(int x) { return x + bar; }
constexpr static int bar = foo(sizeof(int));
};


Related Topics



Leave a reply



Submit