Ответ 1
GCC здесь не так.
Все ссылки на N4431, последний С++ WD.
[tl; dr: существует разница между функцией, являющейся встроенной (или, точнее, являющейся встроенной функцией, как определено в 7.1.2/2) и объявляется спецификатором inline
. Спецификатор constexpr
делает функцию встроенной, но не является спецификатором inline
.]
Спецификаторы описаны в подпункте 7.1 стандарта С++ и являются элементом грамматики. Поэтому, когда стандартные сообщения о спецификаторе foo
появляются где-то, это означает, что спецификатор буквально появился в исходном коде (синтаксический анализ) исходного кода. Спецификатор inline
- это спецификатор функции, описанный в подпункте 7.1.2, и его эффект заключается в том, чтобы сделать функцию встроенной функцией. (7.1.2)/2:
Объявление функции (8.3.5, 9.3, 11.3) с спецификатором
inline
объявляет встроенную функцию.
Существует два других способа объявить встроенную функцию без использования спецификатора inline
. Один из них описан в (7.1.2)/3:
Функция, определенная в определении класса, является встроенной функцией.
Другое описано в (7.1.5)/1:
Функции constexpr и конструкторы constexpr неявно inline (7.1.2).
Ни один из них не говорит о том, что поведение выглядит так, как если бы существовал спецификатор inline
, просто что функция является встроенной функцией.
Итак, почему это правило существует?
В более простой форме этого правила в (7.1.2)/3:
Если спецификатор
inline
используется в объявлении друга, это объявление должно быть определением или функция должна быть объявлена ранее.
Цель этого заключается в том, чтобы разрешать объявления друзей в большинстве случаев игнорироваться - им не разрешено добавлять "новую информацию" к подневольному сущности, за исключением особого случая, когда они определяют функцию друга. (Это, в свою очередь, позволяет реализовать замедление анализа определения класса до тех пор, пока оно не "понадобится".) Таким образом, мы также видим в (8.3.6)/4:
Если в объявлении друга указано выражение аргумента по умолчанию, это объявление должно быть определением и должно быть единственным объявлением шаблона функции или функции в блоке перевода.
И то же самое относится к объявлению специализации друга шаблона функции: если он мог бы добавить дополнительную информацию, то реализации не могли бы задерживать анализ определения класса.
Теперь обратите внимание, что это рассуждение не относится к constexpr
: если спецификатор constexpr
появляется в любом объявлении функции, он должен появляться в каждом объявлении, в соответствии с (7.1.5)/1. Поскольку здесь нет "новой информации", нет необходимости в ограничении.