Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Static member access in constant expressions

Accessing static class member functions or variables, can be done in two ways: through an object (obj.member_fun() or obj.member_var) or through the class (Class::member_fun() or Class::member_var). However, in constexpr functions, Clang gives an error on the object access and requires to use class access:

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

#define TEST 1

constexpr auto foo(S const& s [[maybe_unused]]) 
{
#if TEST
    constexpr auto v = s.v();   // ERROR for clang, OK for gcc
#else    
    constexpr auto v = S::v();  // OK for clang and gcc
#endif
    return v;
}

constexpr auto bar(S const& s [[maybe_unused]])
{
#if TEST   
    constexpr auto v = s.s_v;   // ERROR for clang, OK for gcc
#else    
    constexpr auto v = S::s_v;  // OK for clang and gcc
#endif    
    return v;
}

int main() {}

Live Example compiled with -std=c++1z and #define TEST 1 for Clang 5.0 SVN, with error message:

Start
prog.cc:12:24: error: constexpr variable 'v' must be initialized by a constant expression
    constexpr auto v = s.v();   // ERROR for clang, OK for gcc
                       ^~~~~
prog.cc:22:24: error: constexpr variable 'v' must be initialized by a constant expression
    constexpr auto v = s.s_v;   // ERROR for clang, OK for gcc
                       ^~~~~
2 errors generated.
1
Finish

Question: is this is a Clang bug, or is gcc too liberal in accepting both syntax forms for static member access in a constexpr function?

like image 419
TemplateRex Avatar asked Jul 09 '17 10:07

TemplateRex


People also ask

Can a static member function be const?

A static member function cannot be declared with the keywords virtual , const , volatile , or const volatile . A static member function can access only the names of static members, enumerators, and nested types of the class in which it is declared.

How static members can be accessed?

A static member function can be called even if no objects of the class exist and the static functions are accessed using only the class name and the scope resolution operator ::. A static member function can only access static data member, other static member functions and any other functions from outside the class.

Can I access static variable in another function?

Static variables in methods i.e. you cannot use a local variable outside the current method which contradicts with the definition of class/static variable. Therefore, declaring a static variable inside a method makes no sense, if you still try to do so, a compile time error will be generated.

How are static members accessed in C++?

Static data members are declared inside the class but they are initialized outside of the class. Static member functions can be accessed using class name and scope resolution. Also, we can call the member function without creating any object of the class.


1 Answers

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() {}
like image 149
StoryTeller - Unslander Monica Avatar answered Oct 05 '22 22:10

StoryTeller - Unslander Monica