Данный :
#include <concepts>
#include <iostream>
template<class T>
struct wrapper;
template<std::signed_integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "signed_integral" << std::endl;
}
};
template<std::integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "integral" << std::endl;
}
};
int main()
{
wrapper<int> w;
w.print(); // Output : signed_integral
return 0;
}
Из приведенного выше кода, int
подходит как для концепции, так std::integral
и для std::signed_integral
концепции.
Удивительно, но это компилирует и печатает «signature_integral» на компиляторах GCC и MSVC. Я ожидал, что он потерпит неудачу с ошибкой в духе «специализация шаблона уже определена».
Хорошо, это законно, достаточно справедливо, но почему был std::signed_integral
выбран вместо std::integral
? Существуют ли какие-либо правила, определенные в стандарте, с помощью которых выбирается специализация шаблона, когда несколько аргументов соответствуют критериям шаблона?
c++
language-lawyer
c++20
c++-concepts
Льюис Лиман
источник
источник
Ответы:
Это связано с тем, что концепции могут быть более специализированными, чем другие, что-то вроде порядка шаблонов. Это называется частичным упорядочением ограничений
В случае понятий они объединяют друг друга, когда включают эквивалентные ограничения. Например, вот как
std::integral
иstd::signed_integral
реализованы:Нормализуя ограничения, компилятор сводит выражение сравнения к следующему:
В этом примере
signed_integral
подразумеваетсяintegral
полностью. Таким образом, в некотором смысле подписанный интеграл «более ограничен», чем интеграл.Стандарт пишет это так:
Из [temp.func.order] / 2 (выделено мое):
Это означает, что если существует несколько возможных замен для шаблона, и оба они выбраны из частичного упорядочения, он выберет наиболее ограниченный шаблон.
Из [temp.constr.order] / 1 :
Это описывает алгоритм использования, который компилятор использует для упорядочения ограничений, и, следовательно, концепции.
источник
C ++ 20 имеет механизм для принятия решения, когда одна конкретная ограниченная сущность «более ограничена», чем другая. Это не простая вещь.
Это начинается с концепции разбиения ограничения на его атомарные компоненты, процесс, называемый нормализацией ограничения . Это большой и слишком сложный вопрос, но основная идея состоит в том, что каждое выражение в ограничении рекурсивно разбивается на его элементарные концептуальные части, пока вы не достигнете под-выражения компонента, которое не является концептом.
Поэтому , учитывая , что взгляд давайте на то, как
integral
иsigned_integral
понятия определены :Разложение
integral
простоis_integral_v
. Разложениеsigned_integral
естьis_integral_v && is_signed_v
.Теперь мы подошли к понятию подчинения ограничения . Это довольно сложно, но основная идея состоит в том, что ограничение C1, как говорят, «включает» ограничение C2, если разложение C1 содержит каждое подвыражение в C2. Мы можем видеть , что
integral
не подводитьsigned_integral
, ноsigned_integral
делает подводитьintegral
, так как она содержит всеintegral
делает.Далее мы подходим к упорядочению ограниченных объектов:
Поскольку
signed_integral
включает в себяintegral
,<signed_integral> wrapper
«по крайней мере так же ограничен», как<integral> wrapper
. Тем не менее, обратное неверно из-за необратимого включения.Следовательно, в соответствии с правилом для «более ограниченных» объектов:
Поскольку,
<integral> wrapper
по крайней мере, оно не так ограничено<signed_integral> wrapper
, последнее считается более ограниченным, чем первое.И поэтому, когда они оба могут подать заявку, побеждает более ограниченное объявление.
Имейте в виду, что правила подчинения ограничения прекращаются, когда встречается выражение, которое не является
concept
. Так что, если вы сделали это:В этом случае
my_signed_integral
не определили быstd::integral
. Хотяmy_is_integral_v
определяется идентичноstd::is_integral_v
, что оно , поскольку это не концепция, правила подстановки в C ++ не могут просмотреть его, чтобы определить, что они одинаковы.Таким образом, правила подчинения побуждают вас строить концепции из операций над атомарными концепциями.
источник
С Partial_ordering_of_constraints
а также
И концепция
std::signed_integral
включает в себяstd::integral<T>
концепцию:Итак, ваш код в порядке, так как
std::signed_integral
он более «специализирован».источник