Подтвердить что ты не робот

Constexpr не разрешено в объявлении специализации шаблона друга?

Я переношу кодовую базу С++ 14- constexpr от Clang до последнего g++ - 5.1. Рассмотрим приведенный ниже фрагмент кода самодельного класса bitset, который правильно компилируется с halcyon дней Clang 3.3 (почти через 2 года!)

#include <cstddef>

template<std::size_t>
class bitset;

template<std::size_t N>
constexpr bool operator==(const bitset<N>& lhs, const bitset<N>& rhs) noexcept;

template<std::size_t N>
class bitset
{
    friend constexpr bool operator== <>(const bitset<N>&, const bitset<N>&) noexcept;
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ <-- error from this piece
};

template<std::size_t N>
constexpr bool operator==(const bitset<N>& /* lhs */, const bitset<N>& /* rhs */) noexcept
{
    return true;
}

int main() {}

Живой пример в Wandbox. Однако g++ - 5.1 и текущий выпуск соединительной линии дают ошибку:

'constexpr' не разрешено в объявлении шаблона друга специализации

Вопрос: это известная ошибка g++ или Clang не соответствует последнему стандарту?

Примечание. В приведенном выше примере используются только функции стиля С++ 11 style constexpr, поскольку внутри operator== не происходит никаких изменений, поэтому кажется, что некоторые странные помехи между шаблонами, друзьями и constexpr,

UPDATE: зарегистрирован как ошибка 65977 в Bugzilla.

4b9b3361

Ответ 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. Поскольку здесь нет "новой информации", нет необходимости в ограничении.