访问静态类成员函数或变量可以通过两种方式完成:通过对象(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
函数中静态成员访问的两种语法形式?
答案 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
)访问类成员。
constexpr
函数作为表达式的一部分,所以“except if”子句在“没有任何”的集合中规则捕获。
现在不要问我是否有任何意义,或者是否有人应该理解......: - )