Должно ли предложение шаблона класса повторяться вне определений членов?

Когда член шаблона класса, который использует предложение requires, определен вне класса, gcc не жалуется, если requires не указан, тогда как clang делает.

Рассмотрим фрагмент кода ниже:

#include <concepts>

template<typename Container>
    requires std::integral<typename Container::value_type>
class Foo {
public:
    void func();
};

template<typename Container>
void Foo<Container>::func()
{}

Компиляция с использованием gcc не жалуется.

В то время как clang сообщает о следующей ошибке:

❯ clang++ -std=c++2a test.cpp
test.cpp:10:1: error: requires clause differs in template redeclaration
template<typename Container>
^
test.cpp:4:19: note: previous template declaration is here
    requires std::integral<typename Container::value_type>
                  ^
1 error generated.

Если я изменю определение, как показано ниже:

template<typename Container>
    requires std::integral<typename Container::value_type>
void Foo<Container>::func()
{}

теперь clang не жалуется.

Вывод из gcc --version:

gcc (GCC) 10.2.0

Вывод из clang --version:

clang version 10.0.1 
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

Следует ли сообщать об этой ошибке?


person PHD    schedule 28.08.2020    source источник


Ответы (2)


Сообщение об ошибке должно быть зарегистрировано для GCC, потому что он принимает код, даже если объявление члена вне класса не имеет эквивалентного заголовка шаблона.

[temp.class]

3 Когда функция-член, класс-член, перечисление элементов , статический член данных или шаблон элемента шаблона класса определены вне определения шаблона класса, определение члена определяется как определение шаблона, в котором template-head эквивалентен шаблону класса ([temp.over.link]).

[temp.over.link]

6 Две головки шаблона эквивалентны, если их шаблон- списки параметров имеют одинаковую длину, соответствующие параметры шаблона эквивалентны и оба объявлены с ограничениями типа, которые эквивалентны, если любой параметр шаблона объявлен с ограничением типа, и , если любой из заголовков шаблона имеет требование -clause, у них обоих есть предложения-требования, и соответствующие выражения-ограничения эквивалентны.

Эквивалентность шаблонов-заголовков требует, чтобы оба имели эквивалентное предложение requires. Отсутствие этого полностью нарушает эквивалентность.

person StoryTeller - Unslander Monica    schedule 28.08.2020
comment
подано здесь: gcc.gnu.org/bugzilla/show_bug.cgi?id=96830 - person PHD; 28.08.2020

Из [temp.mem.func] / 1 [извлечение, акцент мой]:

Функция-член шаблона класса может быть определена вне определения шаблона класса, в котором она объявлена. [Пример:

Ограниченная функция-член может быть определена вне очереди:

template<typename T> concept C = requires {
  typename T::type;
};

template<typename T> struct S {
  void f() requires C<T>;
  void g() requires C<T>;
};

template<typename T>
  void S<T>::f() requires C<T> { }  // OK
template<typename T>
  void S<T>::g() { }                // error: no matching function in S<T>

- конечный пример]

особо отмечая последний пример (ненормативного) текста.

Таким образом, Clang правильно отклоняет, тогда как GCC неправильно принимает первую программу в качестве автономного определения.

template<typename Container>
void Foo<Container>::func() {}

не соответствует ни одной функции в Foo<Container>.

(я еще не нашел открытого отчета об ошибке GCC для этого)

person dfrib    schedule 28.08.2020
comment
Это не плохо сформированный отчет о недоставке. Заголовки шаблонов в примере OP функционально не эквивалентны. Это можно диагностировать. - person StoryTeller - Unslander Monica; 28.08.2020
comment
@ StoryTeller-UnslanderMonica Ах, вы правы - нечетно-функциональная эквивалентность - это то место, где можно остановиться (а не в кроличью нору эквивалентности, как я). Спасибо. - person dfrib; 28.08.2020