Требуется позиционирование предложения в шаблонах функций C++20.

В C++20 вы можете написать шаблон функции с ограничениями несколькими способами:

template <typename T>
concept Fooable = true;

template <typename T>
    requires Fooable<T>
void do_something(T&); // (1)

template <typename T>
void do_something(T&) requires Fooable<T>; // (2)

Согласно принятому ответу в этот вопрос, эти две формы эквивалентны (что всегда было моим пониманием).

Однако я заметил, что GCC 12.1 считает (1) и (2) двумя разными функциями, а не (2) повторным объявлением: можно предоставить определения для обеих, и тогда попытка вызова do_something() будет неоднозначной (пример).

  • Правильно ли GCC, и это две разные функции?
  • Если да, то есть ли технические различия в значении этих двух стилей?

Обновлено:

  • Как указано в комментариях, в связанных вопросах говорится, что объявление и определение шаблона функции должны использовать один и тот же «требуемый стиль». В чем причина этого ограничения?

(Я смутно припоминаю, что во времена Concepts TS требования подвергались «нормализации», чтобы решить, когда они были эквивалентны — я думаю, в C++20 это уже не так?)

В принятом ответе на этот вопрос также говорится: «Невозможно объявить с одним синтаксисом и определить с другим». Возможно, это не является явным, но, безусловно, говорит, что он объявляет разные функции.

StoryTeller - Unslander Monica 16.05.2022 20:11

Хорошо, но почему это так?

Tristan Brindle 16.05.2022 20:18

Ответ юриста, не связанного с языком, заключается в том, что первый может применяться только к спискам параметров шаблона, а второй - к любой функции (которая может не быть шаблоном). Применение второй формы к не-шаблону произойдет, например, если мы отключим (не-шаблонную) функцию-член шаблона класса. Для кроличьей норы LL скоро кто-нибудь появится, я уверен.

StoryTeller - Unslander Monica 16.05.2022 20:20

Конечно, но правило могло бы звучать примерно так: «Форма (1) интерпретируется так, как если бы вы написали ее в форме (2)» (что, как я смутно припоминаю, имело место во времена Концепции ТС?). Вот почему я был немного удивлен, что GCC допускает такую ​​перегрузку.

Tristan Brindle 16.05.2022 20:23

Могло, но не было. Вероятно, по тем же причинам сокращенные шаблоны — это не просто «переписанные» обычные шаблоны, эквивалентности в них тоже нет. И я готов поспорить, что все это, вероятно, будет хорошо сочетаться с абзацами ODR, которые требуют одинакового определения вплоть до последовательности токенов.

StoryTeller - Unslander Monica 16.05.2022 20:31
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
3
5
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Формулировка в этой области немного изменилась. В C++20 у нас было это правило в [temp.over.link]/7:

  1. Two function templates are equivalent if they are declared in the same scope, have the same name, have equivalent template-heads, and have return types, parameter lists, and trailing requires-clauses (if any) that are equivalent using the rules described above to compare expressions involving template parameters. Two function templates are functionally equivalent if they are declared in the same scope, have the same name, accept and are satisfied by the same set of template argument lists, and have return types and parameter lists that are functionally equivalent using the rules described above to compare expressions involving template parameters. If the validity or meaning of the program depends on whether two constructs are equivalent, and they are functionally equivalent but not equivalent, the program is ill-formed, no diagnostic required.

  2. [Note 7: This rule guarantees that equivalent declarations will be linked with one another, while not requiring implementations to use heroic efforts to guarantee that functionally equivalent declarations will be treated as distinct.

// guaranteed to be the same
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+10>);

// guaranteed to be different
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+11>);

// ill-formed, no diagnostic required
template <int I> void f(A<I>, A<I+10>);
template <int I> void f(A<I>, A<I+1+2+3+4>);

-end note]

В вашем примере:

template <typename T>
    requires Fooable<T>
void do_something(T&); // (1)

template <typename T>
void do_something(T&) requires Fooable<T>; // (2)

Они функционально эквивалентны (у них, в основном, одинаковые ограничения), но не эквивалентны (у них разные заголовки шаблонов — предложение require после параметров шаблона является частью заголовка шаблона), что делает этот неправильный формат не требующим диагностики. На практике, поскольку они не эквивалент, это разные перегрузки, но поскольку они функционально эквивалентны, любая попытка вызова между ними будет неоднозначной.

Как я указываю в другом ответе, они имеют одинаковое значение - просто вам нужно придерживаться одной формы для объявления и определения, если вы их разделите.


Формулировка Текущий после сводной статьи Дэвиса Херринга P1787 предполагает переход к [базовая.область.область]/4:

Two declarations correspond if they (re)introduce the same name, both declare constructors, or both declare destructors, unless [...] each declares a function or function template, except when [...] both declare function templates with equivalent non-object-parameter-type-lists, return types (if any), template-heads, and trailing requires-clauses (if any), and, if both are non-static members, they have corresponding object parameters.

Это делает два do_somethings не соответствует, что делает их разными шаблонами функций. Мы не сталкиваемся с новым функционально эквивалентным, но не эквивалентным правилом (поэтому мы не плохо сформированы, диагностика не требуется), но у нас просто есть два шаблона функций, которые обязательно неоднозначны во всех случаях. Так что... не самая полезная вещь на свете.

вау, это то, о чем я даже не подозревал. Рассуждения обретают смысл после прочтения — отсутствие необходимости в диагностике означает, что у меня есть несколько строк кода в некоторых проектах, которые нужно изучить сейчас :(

Cássio Renan 16.05.2022 20:40

Спасибо Барри! Я думал, что существует правило перезаписи, и действительно, в Концепции TS это было повторное объявление (godbolt.org/z/W1a63cP47). Вы знаете, когда/почему это было изменено?

Tristan Brindle 16.05.2022 20:43

@TristanBrindle Да (open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4641.pdf, ищите temp.over.link). Вероятно, потому что это просто добавляет компилятору больше работы для проверки, но на самом деле не добавляет никакой ценности? Точно не знаю, могу спросить у Андрея.

Barry 16.05.2022 20:46

@ Барри, я не знаю, как ты так быстро находишь эти ссылки! IFNDR кажется довольно суровым, хотя

Tristan Brindle 16.05.2022 20:52

Какое правило утверждает, что два рассматриваемых шаблона функций функционально эквивалентны? [temp.over.link]/6 говорит, что два шаблон-головки функционально эквивалентны, если они принимают и удовлетворяются одним и тем же набором списков аргументов шаблона. В случае (2) правильно ли говорить, что шаблон-голова не принимает T, который не является Fooable? Ограничение в этом случае не является частью шаблон-голова.

Brian Bi 16.05.2022 20:57

@BrianBi Теперь это правило могло быть переведено в соответствующую формулировку (угорь.is/С++ черновик/базовый.scope.scope#4.3)

Barry 16.05.2022 21:52

@TristanBrindle Одна проблема здесь, кстати, заключается в том, что template <class T> requires meow<T>(v) void f(T v) и template <class T> void f(T v) requires meow<T>(v) на самом деле могут означать разные вещи, несмотря на то, что они эквивалентны токену (см. Также этот ответ)

Barry 16.05.2022 21:56

[basic.scope.scope]/4.3, похоже, не поддерживает вывод о том, что программа представляет собой отчет о недоставке с неправильным форматом. Списки типов параметров, не являющихся объектными, и возвращаемые типы эквивалентны. шаблон-головки не являются ни эквивалентными, ни функционально эквивалентными, поскольку один из них ограничен, а другой нет. Замыкающие требует-оговорки не являются ни эквивалентными, ни функционально эквивалентными, поскольку один присутствует, а другой нет. Эти четыре критерия рассматриваются отдельно.

Brian Bi 16.05.2022 22:03

@BrianBi Формулировка здесь сильно изменилась, и ее трудно отследить. В статье Пре-Дэвиса применялась функционально эквивалентная формулировка. Теперь, кажется, просто упустить это дело. Я не думаю, что это имеет большое значение, поскольку в конечном итоге эти два не являются эквивалент, но, поскольку они удовлетворяются одними и теми же типами, они «функционально эквивалентны», поэтому запись в обоих направлениях не делает то, что вы хотите.

Barry 16.05.2022 22:36

Я согласен, что это не имеет большого значения, но для целей этого вопроса это действительно влияет на ответ. Простое объявление (1) и (2) в одной и той же единице перевода не делает программу IFNDR. Программа становится IF, если делается попытка вызвать do_something, поскольку разрешение перегрузки никогда не удастся.

Brian Bi 16.05.2022 22:43

Я подозреваю, что намерение (по-прежнему) состоит в том, чтобы они были IFNDR, и что различные эквивалентности следует рассматривать вместе (хотя бы потому, что вы не можете установить эквивалентность компонентов шаблона независимо от его шаблон-голова). Проблема может быть в порядке.

Davis Herring 16.05.2022 23:42

Другие вопросы по теме

Почему для шаблонов функций не разрешена явная специализация с закрытым типом?
Какая версия стандарта С++ позволяет повторно использовать память, ранее занятую объектом класса, который имеет константные или ссылочные члены?
Тернарный оператор, примененный к классу с оператором преобразования и конструктором удаления, вызывает неоднозначность
Инициализировать массив на основе версии C++ и компилятора
Законно ли размещение нового в константной переменной с автоматическим сроком хранения?
Оптимизация и строгое сглаживание
Является ли нарушением строгого сглаживания переход к «суперклассу» и обратно в C?
Указатели на нестатическую функцию-член «формально» не считаются указателями
Пакет параметров упоминается, но не раскрывается в объявлении использования: ошибки компилятора или нет?
Когда компилятору необходимо вычислить псевдоним?