请考虑以下示例代码:
#include <array>
struct MyClass
{
size_t value = 0;
constexpr static size_t size() noexcept
{
return 3;
}
};
template <size_t N>
void DoIt()
{
MyClass h;
std::array<int, h.size()> arr;
}
int main()
{
DoIt<1>();
}
当我尝试使用GCC 7.3.0编译它时,我得到一个关于h在非constexpr上下文中不可用的错误:
cexpr.cpp: In function ‘void DoIt()’:
cexpr.cpp:17:26: error: the value of ‘h’ is not usable in a constant expression
std::array<int, h.size()> arr;
^
cexpr.cpp:16:11: note: ‘h’ was not declared ‘constexpr’
MyClass h;
^
cexpr.cpp:17:27: error: the value of ‘h’ is not usable in a constant expression
std::array<int, h.size()> arr;
^
cexpr.cpp:16:11: note: ‘h’ was not declared ‘constexpr’
MyClass h;
^
但是,当我尝试在Clang 6.0.0中编译完全相同的代码时,它编译时没有任何错误。另外,当我修改代码不在模板化的DoIt()
函数内时,GCC编译就好了:
#include <array>
struct MyClass
{
size_t value = 0;
constexpr static size_t size() noexcept
{
return 3;
}
};
int main()
{
MyClass h;
// this compiles just fine in Clang and GCC
std::array<int, h.size()> arr;
}
我已经知道如何修复第一个代码,所以它使用decltype
在GCC上编译,但我很想知道为什么第一段代码不能用GCC编译?这只是GCC中的一个错误,还是我对使用constexpr静态成员函数有些不了解?
对我来说看起来像个错误。
表达式h.size()
的类型和含义由[expr.ref]
“类成员访问”定义:
[expr.post]/3
缩写postfix-expression.id-expression为
E1.E2
,E1
称为对象表达式。 [...]
和
[expr.post]/6.3.1
如果
E2
是(可能是重载的)成员函数,则使用函数重载决策来确定E1.E2
是指静态成员函数还是非静态成员函数。
- (6.3.1)如果它引用静态成员函数并且
E2
的类型是“参数类型列表返回T
的函数”,那么E1.E2
是一个左值;表达式指定静态成员函数。E1.E2
的类型与E2
的类型相同,即“返回T
的参数类型列表的函数”。
这意味着h.size
具有与::MyClass::size
相同的类型并且被评估为这样,无论h
是否是constexpr
。
然后h.size()
调用constexpr
函数,并且根据[expr.const]/4
是核心常数表达式。