Один из углов концепций C ++ 20 заключается в том, что есть определенные ситуации, в которых вы должны писать requires requires
. Например, этот пример из [expr.prim.req] / 3 :
Требуется выражение также может быть использовано в требуете-положение ([Темп]) в качестве способа написания специальных ограничений на аргументах шаблона , такие как один ниже:
template<typename T> requires requires (T x) { x + x; } T add(T a, T b) { return a + b; }
Первый требует вводит предложение require , а второй вводит выражение require .
Какова техническая причина, по которой нужно второе requires
ключевое слово? Почему мы не можем просто разрешить писать:
template<typename T>
requires (T x) { x + x; }
T add(T a, T b) { return a + b; }
(Примечание: пожалуйста, не отвечайте, что это грамматика requires
)
c++
c++-concepts
c++20
Барри
источник
источник
noexcept(noexcept(...))
.requires
моему мнению, это два омонима: они выглядят одинаково, пишутся одинаково, пахнут одинаково, но по своей сути различны. Если бы я предложил исправление, я бы предложил переименовать один из них.co_requires
? (Извините, не удержался).long long
.Ответы:
Это потому, что грамматика требует этого. Оно делает.
requires
Ограничение не должно использоватьrequires
выражение. Он может использовать любое более или менее произвольное логическое константное выражение. Следовательно,requires (foo)
должно быть законноеrequires
ограничение.requires
Выражение (та вещь , что тесты некоторые вещи следуют ли определенные ограничения) является идеальной отправной точкой конструкт; это просто введено тем же ключевым словом.requires (foo f)
будет началом правильногоrequires
выражения.То, что вы хотите, это то, что если вы используете
requires
в месте, которое принимает ограничения, вы должны быть в состоянии сделать «ограничение + выражение» изrequires
предложения.Итак, вот вопрос: если вы поместите
requires (foo)
в место, подходящее для ограничения требует ... как далеко должен пройти анализатор, чтобы понять, что это ограничение требуется а не выражение constraint + так, как вы этого хотите быть?Учти это:
Если
foo
это тип, то(foo)
это список параметров выражения требует, и все в{}
является не телом функции, а телом этогоrequires
выражения. В противном случае,foo
является выражением вrequires
предложении.Ну, вы могли бы сказать, что компилятор должен просто выяснить, что
foo
является первым. Но C ++ действительно не нравится, когда базовый акт парсинга последовательности токенов требует, чтобы компилятор выяснил, что означают эти идентификаторы, прежде чем он сможет понять токены. Да, C ++ является контекстно-зависимым, так что это происходит. Но комитет предпочитает избегать этого там, где это возможно.Так что да, это грамматика.
источник
requires
появляется после<>
набора аргументов шаблона или после списка параметров функции, это предложение require. Еслиrequires
появляется, где выражение является действительным, то это выражение require. Это может быть определено структурой дерева разбора, а не содержимым дерева разбора (специфика определения идентификатора будет содержимым дерева).concept
иrequires
. Представление третьего, когда второе сможет охватить оба случая без грамматических проблем и небольшого количества проблем, с которыми сталкиваются пользователи, является просто расточительным. В конце концов, единственная визуальная проблема заключается в том, что ключевое слово повторяется дважды.Ситуация в точности аналогична
noexcept(noexcept(...))
. Конечно, это больше похоже на плохую вещь, чем на хорошую, но позвольте мне объяснить. :) Начнем с того, что вы уже знаете:В C ++ 11 есть «
noexcept
-clauses» и «-expressions»noexcept
. Они делают разные вещи.В
noexcept
-классе говорится: «Эта функция не должна быть исключена, когда ... (некоторое условие)». Он идет по объявлению функции, принимает логический параметр и вызывает поведенческое изменение в объявленной функции.noexcept
-Expression говорит, «Compiler, скажите , пожалуйста , является ли (некоторое выражение) является noexcept.» Это само логическое выражение. У него нет «побочных эффектов» на поведение программы - он просто запрашивает у компилятора ответ на вопрос «да / нет». "Это выражение не исключение?"Мы можем гнездятся в
noexcept
-expression внутриnoexcept
-clause, но мы , как правило , считают это плохой стиль , чтобы сделать это.Считается лучшим стилем инкапсулировать
noexcept
выражение в признаке типа.Рабочий проект C ++ 2a имеет «
requires
-clauses» и «-expressions»requires
. Они делают разные вещи.В
requires
-классе говорится: «Эта функция должна участвовать в разрешении перегрузки, когда ... (некоторое условие)». Он идет по объявлению функции, принимает логический параметр и вызывает поведенческое изменение в объявленной функции.requires
-Expression говорит, «Compiler, скажите , пожалуйста , является ли это (некоторый набор выражений) хорошо сформированы.» Это само логическое выражение. У него нет «побочных эффектов» на поведение программы - он просто запрашивает у компилятора ответ на вопрос «да / нет». "Это выражение правильно сформировано?"Мы можем гнездятся в
requires
-expression внутриrequires
-clause, но мы , как правило , считают это плохой стиль , чтобы сделать это.Считается лучшим стилем инкапсулировать
requires
выражение в признаке типа ...... или в концепции (рабочий проект C ++ 2a).
источник
noexcept
есть проблема , чтоnoexcept(f())
может означать либо интерпретироватьf()
как логические , который мы используем , чтобы установить спецификации или проверить , является ли или неf()
являетсяnoexcept
.requires
не имеет этой двусмысленности, потому что выражения, проверяющие правильность, уже должны быть введены с{}
s. После этого аргумент в основном звучит так: «Грамматика так говорит».{}
являются необязательными.{}
являются обязательными, это не то, что показывает этот комментарий. Тем не менее, это отличный комментарий, демонстрирующий разбор неоднозначности. Возможно, принял бы этот комментарий (с некоторыми пояснениями) как самостоятельный ответrequires is_nothrow_incrable_v<T>;
должно бытьrequires is_incrable_v<T>;
Я думаю, что страница концепций cppreference объясняет это. Я могу объяснить с помощью «математики», почему это должно быть так:
Если вы хотите определить концепцию, вы делаете это:
Если вы хотите объявить функцию, которая использует эту концепцию, вы делаете это:
Теперь, если вы не хотите определять концепцию отдельно, я думаю, все, что вам нужно сделать, это какая-то замена. Возьмите эту часть
requires (T x) { x + x; };
и замените ееAddable<T>
, и вы получите:о чем ты спрашиваешь.
источник
template<typename T> requires (T x) { x + x; }
и требовать, чтобы требование могло быть и предложением, и выражением?requires
-as-ограничений не обязательно должно бытьrequires
выражением. Это всего лишь одно из возможных применений.requires requires
. Они могли бы добавить что-то в грамматику,template<typename T> requires (T x) { x + x; }
но не сделали этого. Барри хочет знать, почему они этого не сделалиtemplate<class T> void f(T) requires requires(T (x)) { (void)x; };
означает что-то другое, если вы удалите один изrequires
es.Я нашел комментарий от Эндрю Саттона (одного из авторов концепции, который реализовал его в gcc) весьма полезным в этом отношении, поэтому я подумал, что просто процитирую его здесь почти полностью:
источник
Потому что вы говорите, что у вещи A есть требование B, а у требования B есть требование C.
Вещь A требует B, который в свою очередь требует C.
Предложение «требует» само по себе требует чего-то.
У вас есть вещь A (требующая B (требующая C)).
Мех. :)
источник
requires
концептуально не одно и то же (один - это предложение, другой - выражение). На самом деле, если я правильно понимаю, два набора()
inrequires (requires (T x) { x + x; })
имеют очень разные значения (внешнее является необязательным и всегда содержит логический constexpr; внутреннее является обязательной частью введения выражения require и не допускает фактических выражений).