Основные понятия C++20: нормализация ограничений

Это пример из стандарта С++ 20 (ISO/IEC 14882:2020), раздел 13.5.4 ([temp.constr.normal]), пункт 1 (выделено мной):

Нормальная форма идентификатора концепта C<A1, A2, ..., An> является нормальной формой выражения ограничения C после замены A1, A2, ..., An на соответствующие параметры шаблона C в отображения параметров в каждом атомарном ограничении. Если любая такая замена приводит к недопустимому типу или выражению, программа неправильно сформирована; диагностика не требуется.
template<typename T> concept A = T::value || true;
template<typename U> concept B = A<U*>;
template<typename V> concept C = B<V&>;

Я понимаю, что C делает программу неправильной (и почему). Однако мне не ясно, приведет ли B к тому, что программа будет неправильно сформирована или нет. В тексте говорится, что нормализация B действительна, но в то же время утверждается, что выражение T::value неправильно сформировано из-за этого типа указателя (что я понимаю). Означает ли это, что действительна только часть нормализации процесса, но сама программа неправильно сформирована на более позднем этапе при проверке T::value? Или программа действительна в любом случае и проверка T::value как-то пропускается/избегается?

Я проверил с помощью Godbolt Compiler Explorer, и GCC, и Clang, похоже, с этим справляются. Тем не менее, поскольку в стандарте сказано, что «диагностика не требуется», это не очень помогает.

«мне не ясно, приведет ли B к тому, что программа будет неправильно сформирована или нет». Это прямо говорит, что она действительна; Что еще тебе нужно?

Nicol Bolas 14.02.2023 02:51

Amir Kirsh 14.02.2023 04:09
Типы данных JavaScript
Типы данных JavaScript
В JavaScript существует несколько типов данных, включая примитивные типы данных и ссылочные типы данных. Вот краткое объяснение различных типов данных...
Как сделать движок для футбольного матча? (простой вариант)
Как сделать движок для футбольного матча? (простой вариант)
Футбол. Для многих людей, живущих на земле, эта игра - больше, чем просто спорт. И эти люди всегда мечтают стать футболистом или менеджером. Но, к...
Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик
CSS Flex: что должен знать каждый разработчик Модуль flexbox, также известный как гибкий модуль разметки box, помогает эффективно проектировать и...
Введение в раздел &quot;Заголовок&quot; в HTML
Введение в раздел "Заголовок" в HTML
Говорят, что лучшее о человеке можно увидеть только изнутри, и это относится и к веб-страницам HTML! Причина, по которой некоторые веб-страницы не...
14
2
1 426
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Концепция B действительна, так как вы можете передать указатель на концепцию A. Внутри самой A указатель не может получить доступ к ::value, но это, согласно спецификации, [temp.constr.atomic], не будет рассматриваться как ошибка, а скорее как false, тогда || true в понятии A составит полное выражение true.


Попытка оценить неправильно сформированное выражение, не требующее диагностики, с использованием static_assert не обязательно поможет ответить на вопрос, является ли выражение допустимым или нет, поскольку компилятору не требуется сбой static_assert на неправильно сформированном- выражение, не требующее диагностики.

Обратите внимание, что каждое нормализованное ограничение состоит из 2 частей: Атомарное ограничение и связанное сопоставление параметров.


Давайте разделим каждое ограничение на эти две части для ваших трех примеров концепций:

В вашем примере нормализованная форма понятия A будет дизъюнкцией этих двух ограничений:

  • Атомное выражение: X::value
    Отображение параметров: X ↦ T
  • Атомное выражение: true
    Нет сопоставления параметров

Нормализованная форма понятия B будет дизъюнкцией этих двух ограничений:

  • Атомное выражение: X::value
    Отображение параметров: X ↦ U*
  • Атомное выражение: true
    Нет сопоставления параметров

И нормализованная форма понятия C будет дизъюнкцией этих двух ограничений:

  • Атомное выражение: X::value
    Отображение параметров: X ↦ V&*
  • Атомное выражение: true
    Нет сопоставления параметров

Как формируются сопоставления параметров

Формирование сопоставления параметров для атомарного выражения очень просто:

По умолчанию атомарное выражение всегда начинается с сопоставления параметров идентификации (т. е. без изменений типа):

13.5.4 Нормализация ограничений [[temp.constr.normal]] (1) Нормальная форма выражения E — это ограничение, которое определяется следующим образом: [...] (1.5) Нормальной формой любого другого выражения E является атомарное ограничение, выражением которого является E, а сопоставление параметров является отображением идентичности.

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

13.5.4 Нормализация ограничений [[temp.constr.normal]] (1.4) Нормальная форма идентификатора концепта C<A1, A2, ..., An> — это нормальная форма выражения ограничения C после замены A1, A2, ..., An на соответствующие параметры шаблона C в сопоставлениях параметров в каждом атомарном ограничении. [...]

Вот несколько примеров:

template<class T> constexpr bool always_true = true;

// Atomic constraint: always_true<X>
// Parameter mapping: X ↦ T (identity)
template<class T> concept Base = always_true<T>;

// Atomic constraint: always_true<X>
// Parameter mapping: X ↦ U (identity) 
template<class U> concept Foo = Base<U>;

// Atomic constraint: always_true<X>
// Parameter mapping: X ↦ V::type
template<class V> concept Bar = Base<typename V::type>;

// Atomic constraint: always_true<X>
// Parameter mapping: X ↦ W&&
template<class W> concept Baz = Base<W&&>;

Ваш цитируемый раздел

Что возвращает нас к вашему исходному цитируемому разделу:

13.5.4 Нормализация ограничений [[temp.constr.normal]] (1.4) Нормальная форма идентификатора концепта C<A1, A2, ..., An> — это нормальная форма выражения ограничения C после замены A1, A2, ..., An на соответствующие параметры шаблона C в сопоставлениях параметров в каждом атомарном ограничении. Если любая такая замена приводит к недопустимому типу или выражению, программа неправильно сформирована; диагностика не требуется.

Обратите внимание, что выделенный оператор применяется только к сопоставлению параметров, а не к самому атомарному выражению.

Обратите внимание, что фактический тип, который заменяется на V, не имеет значения на этапе нормализации; единственное, что имеет значение, это то, что само отображение образует недопустимый тип или выражение.

Вот еще несколько примеров:

template<class T> constexpr bool always_true = true;

// well-formed
// Atomic constraint: always_true<X>
// Parameter mapping: X ↦ T (identity)
template<class T> concept Base = always_true<T>;

// well-formed
// Atomic constraint: always_true<X>
// Parameter mapping: X ↦ U::type
template<class U> concept Foo = Base<typename U::type>;

// ill-formed, ndr (invalid parameter mapping)
// Atomic constraint: always_true<X>
// Parameter mapping: X ↦ V*::type
template<class V> concept Bar = Foo<V*>;

// ill-formed, ndr (invalid parameter mapping)
// Atomic constraint: always_true<X>
// Parameter mapping: X ↦ W&*
template<class W> concept Baz = Foo<W&>;

Примерная хронология событий во время компиляции

Чтобы ответить на вопрос, когда ваша программа получает неверный формат ndr, нам нужно установить порядок, в котором происходят события во время компиляции.

  • Когда определяются ограничения связанной декларации ИЛИ оценивается значение концепции, происходит нормализация ограничений. Это дается:

    13.5.4 Нормализация ограничений [[temp.constr.normal]] [Note 1] Нормализация ограничений-выражений выполняется при определении связанных ограничений объявления и при оценке значения id-выражения, именующего специализацию концепта.

    Именно здесь ваша программа станет неправильно сформированной, ndr, если сопоставление параметров образует недопустимый тип или выражение.

  • После того, как ограничения были нормализованы, фактический тип будет заменен ограничениями:

    13.5.2.3 Атомарные ограничения [[temp.constr.atomic]] (3) Чтобы определить, выполняется ли атомарное ограничение, сопоставление параметров и аргументы шаблона сначала подставляются в его выражение. Если подстановка приводит к недопустимому типу или выражению, ограничение не выполняется.

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


Заключение

Итак, чтобы ответить на ваши вопросы:

  • Означает ли это, что действительна только часть нормализации процесса, но сама программа неправильно сформирована на более позднем этапе при проверке T::value?

    Понятия A и B правильно сформированы. Концепция C неправильно сформирована, ndr в процессе нормализации. Фактическое атомарное ограничение T::value в этом случае не имеет значения; это может быть просто always_true<T>.

  • Или программа действительна в любом случае и проверка T::value каким-то образом пропускается/избегается?

    Программа действительна до тех пор, пока концепция C никогда не нормализуется. т. е. его явное вычисление или использование в качестве ограничения сделало бы вашу программу неправильно сформированной, ndr.

    Пример:

    // evaluates concept C
    //   -> results in normalization of C
    //   -> ill-formed, ndr
    static_assert(C</* something */>); 
    
    template<C T>
    void foo() {}
    
    // constraints of foo will be determined
    //   -> results in normalization of C
    //   -> ill-formed, ndr
    foo</* something */>();  
    

Сопоставление учитывает только параметры шаблона, которые появляются в пределах ограничения, поэтому сопоставление true пусто.

T.C. 14.02.2023 15:35

@Т.С. спасибо, я полностью пропустил эту часть в 13.5.2.3 (1). Я обновил свой пост, теперь он должен быть правильным :)

Turtlefight 14.02.2023 19:12

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