静态成员访问常量表达式

Tem*_*Rex 9 c++ clang static-members constexpr c++17

访问静态类成员函数或变量可以通过两种方式完成:通过对象(obj.member_fun()obj.member_var)或通过类(Class::member_fun()Class::member_var).但是,在constexpr函数中,Clang在对象访问时出错,需要使用类访问:

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() {}
Run Code Online (Sandbox Code Playgroud)

使用-std=c++1z#define TEST 1为Clang 5.0 SVN编译的实例,错误消息:

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
Run Code Online (Sandbox Code Playgroud)

问题:这是一个Clang错误,还是gcc过于自由地接受constexpr函数中静态成员访问的两种语法形式?

Sto*_*ica 10

Clang似乎是对的.使用成员访问语法[class.static/1]访问静态成员时:

可以使用qualified-id表达式X :: s来引用类X的静态成员; 没有必要使用类成员访问语法来引用静态成员.可以使用类成员访问语法来引用静态成员,在这种情况下,评估对象表达式.

因此s.v()将导致s评估.现在,根据[expr.const/2.11],s不是一个常量表达式:

2表达式e是核心常量表达式,除非根据抽象机器的规则评估e将评估以下表达式之一:

[...]

一个id-expression,引用引用类型的变量或数据成员,除非引用具有前面的初始化并且:
(2.11.1) - 它用常量表达式初始化或
(2.11.2) - 它的生命周期始于评估e;

s没有前面的初始化与常量表达式,不在范围内foo.


如果要基于函数参数访问静态成员,而不对类型进行硬编码,则前进的方法是std::remove_reference_t<decltype(s)>.这是Clang和GCC都接受的:

#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() {}
Run Code Online (Sandbox Code Playgroud)