Использование статической функции-члена Constexpr

Рассмотрим следующий пример кода:

#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 нельзя использовать в контексте non-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() код, чтобы он не DoIt() в шаблонную 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;
}

Я уже знаю, как исправить первый код, чтобы он компилировался в GCC с использованием decltype, но мне любопытно узнать, почему первый фрагмент кода не компилируется с GCC? Это просто ошибка в GCC, или я что-то не понимаю в использовании статических функций-членов constexpr?

Ответ 1

Похоже, ошибка для меня.

Тип и значение выражения 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 является lvalue; выражение обозначает статическую функцию-член. Тип E1.E2 аналогичен типу E2, а именно "функция списка параметров-типа, возвращающего T ".

Это означает, что h.size имеет тот же тип, что и ::MyClass::size и оценивается как таковой, независимо от того, является constexpr h constexpr или нет.

h.size() является вызовом функции constexpr и является выражением основной константы в соответствии с [expr.const]/4.