constexpr静态成员函数用法

Constexpr static member function usage

考虑以下示例代码:

#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]定义 "Class member access":

[expr.post]/3

Abbreviating postfix-expression.id-expression as E1.E2, E1 is called the object expression. [...]

[expr.post]/6.3.1

If E2 is a (possibly overloaded) member function, function overload resolution is used to determine whether E1.E2 refers to a static or a non-static member function.

  • (6.3.1) If it refers to a static member function and the type of E2 is “function of parameter-type-list returning T”, then E1.E2 is an lvalue; the expression designates the static member function. The type of E1.E2 is the same type as that of E2, namely “function of parameter-type-list returning T”.

这意味着 h.size::MyClass::size 具有相同的类型并且被这样计算,不管 hconstexpr 或不是.

h.size() 是对 constexpr 函数的调用,根据 [expr.const]/4.[=34= 是 核心常量表达式 ]