Встроенное определение функции constexpr законно или нет? gcc (ok) vs clang (ошибка)

Моя текущая программа отклоняется clang, но компилируется с gcc. Это сводится к следующему упрощенному примеру:

struct A {
  static constexpr inline int one();
};

inline constexpr int A::one() { return 1; }

int main() {
  return 0;
}

g++ 4.7.2 компилирует его без ошибок (g++ -std=c++11 -Wall -g -o main example.cpp). clang++ 3.1 отклоняет его:

$ clang++ -std=c++11 -Wall -g -o main example.cpp 
example.cpp:6:25: error: conflicting types for 'one'
inline constexpr int A::one() { return 1; }
                        ^
example.cpp:3:31: note: previous declaration is here
  static constexpr inline int one();
                              ^
1 error generated.

Моя ставка заключается в том, что gcc прав, а clang - это неправильно? Программа должна быть легальной С++ 11.

Интересный побочный эффект. Если one реализуется внутри структуры, clang больше не жалуется:

struct A {
  static constexpr inline int one() { return 1; }
}

gcc также принимает этот вариант. По моему мнению, обе версии должны быть идентичными в соответствии со стандартом. Это ошибка clang или я что-то не хватает?

Ответы

Ответ 1

Хотя в стандарте явно не указано, может ли определение статической функции-члена constexpr быть отдельным от его объявления, оно имеет следующий пример отдельного определения конструктора constexpr в разделе 7.1. 5P1:

struct pixel {
  int x;
  int y;
  constexpr pixel(int); // OK: declaration
};
constexpr pixel::pixel(int a)
  : x(square(a)), y(square(a)) // OK: definition
  { }

Итак, кажется очевидным, что функции constexpr могут иметь отдельное объявление и определение. Также в 7.1.5p1:

Если какое-либо объявление функции или функции имеет спецификатор constexpr, то все его объявления должны содержать constexprСпецификатор.

Это означает, что функция constexpr может иметь (несколько) объявлений без определения.

Ответ 2

Это была ошибка Clang (исправлена ​​в Clang 3.2). Проблема заключалась в том, что Кланг неправильно обрабатывал влияние неявного const ness при определении того, соответствует ли переопределение функции предыдущему объявлению. Рассмотрим:

struct A {
  int f();                  // #1
  constexpr int f() const;  // #2 (const is implicit in C++11 and can be omitted)
  static constexpr int g(); // #3
};

int A::f() { return 1; }           // #4, matches #1
constexpr int A::f() { return 1; } // #5, matches #2, implicitly const
constexpr int A::g() { return 1; } // #6, matches #3, not implicitly const

При сопоставлении объявления вне класса класса 5 с членами A у компилятора есть проблема: он не знает, какого типа новое объявление A::f пока имеет. Если A::f - нестатическая функция-член, то ее тип int () const, и если это статическая функция-член, то ее тип int () (неявный const).

Clang 3.1 не понял это совершенно правильно: он предположил, что если функция constexpr была функцией-членом, то constexpr сделала это неявно const, что позволяет # 4 и # 5 работать, но ломается # 6. Clang 3.2 исправляет это, реализуя правило constexpr -implies- const дважды: один раз в сопоставлении повторного выделения (такой, что # 5 рассматривается как redeclare # 2, а не # 1, хотя он еще неявно const) и снова после того, как было выбрано предыдущее объявление (чтобы добавить неявный const к # 5).

Ответ 3

Я уверен, что g++ верен. На самом деле это было bug в g++. Я не могу найти место в стандарте, в котором явно говорится, что вы можете иметь статическое объявление constexpr отдельно от определения, но если вы посмотрите в разделе 7.1.5, в котором говорится о спецификаторе constexpr (приведено здесь), это не исключает, что обычно означает, что это разрешено.