静态成员访问常量表达式

时间:2017-07-09 10:01:27

标签: 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() {}
对于Clang 5.0 SVN,使用-std=c++1z#define TEST 1编译了

Live Example,并显示错误消息:

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

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

2 个答案:

答案 0 :(得分:9)

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

  

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

因此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() {}

答案 1 :(得分:0)

constexpr auto v = s.v();   // ERROR for clang, OK for gcc

我想这取决于你是用C ++ 11还是C ++ 14模式编译的。如果你查看cppreference,你会发现(我强调的是):

  

核心常量表达式是没有以下任何一种的任何表达式   (...)
  6)this指针,除非用于非静态成员函数内的类成员访问(直到C ++ 14)   6)this指针,除了constexpr函数或constexpr构造函数作为表达式的一部分进行评估(自C ++ 14以来)< / p>

因此,在C ++ 11中,s.v()内发生的任何事情都不会被视为常量表达式,因为它使用this指针,但它 a非静态成员函数(它是static)访问类成员。

然而,根据C ++ 14,它会是,因为它正在评估constexpr函数作为表达式的一部分,所以“except if”子句在“没有任何”的集合中规则捕获。

现在不要问我是否有任何意义,或者是否有人应该理解......: - )