Встроенное определение функции 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 (приведено здесь), это не исключает, что обычно означает, что это разрешено.