【问题标题】:Constexpr static member function usageconstexpr 静态成员函数用法
【发布时间】:2019-07-05 23:16:15
【问题描述】:

考虑以下示例代码:

#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 静态成员函数有什么不明白的地方?

【问题讨论】:

  • 为什么不MyClass::size()
  • 您可以提出一个论点,因为二进制 . 表达式的左侧仍然需要在解析之前作为表达式进行评估(例如,我可以这样做 (MyClass{} + MyClass{}).size()如果 MyClass 有一个 operator+()h 表达式没有副作用这一事实可能会在编译过程中相当晚地崩溃,因此要求它为 constexpr 并非完全不合理。跨度>
  • @Frank 如果是这样,我不明白为什么当我在模板函数的上下文中使用 h.size() 时它无法编译,但在非它编译得很好的模板化函数。
  • 对于通过非constexpr 对象调用静态成员函数是否是常量表达式,实现者/专家之间存在一些不确定性。你不应该能够在常量表达式中使用这样的变量,但静态成员函数当然不会真正使用它。

标签: c++ c++14 language-lawyer static-methods constexpr


【解决方案1】:

在我看来是个错误。

表达式h.size()的类型和含义由[expr.ref]“类成员访问”定义:

[expr.post]/3

缩写postfix-expression.id-expressionE1.E2E1称为对象表达式。 [...]

[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核心常量表达式

【讨论】:

  • 好笑,MyClass h; std::array&lt;int, h.size()&gt; arr; 被 gcc 中继拒绝,但std::array&lt;int, MyClass{}.size()&gt; arr; 被接受:godbolt.org/z/GtStLT
  • 仅供参考,在 gcc-7.3 中,std::array 具有触发错误的非静态数据成员。看:godbolt.org/z/Cz38hS
猜你喜欢
  • 2012-07-16
  • 1970-01-01
  • 2014-03-16
  • 2016-10-15
  • 1970-01-01
  • 2016-07-29
  • 1970-01-01
  • 2015-02-25
  • 2016-12-05
相关资源
最近更新 更多