Я не знаю лучшего способа написать вашу концепцию Vec
, чем со специализацией is_vector
.Но VecInt
можно упростить до:
template <typename T>
concept bool VecInt =
Vec<T> && std::is_same_v<typename T::value_type, int>;
(Кроме того, доступная экспериментальная поддержка концепций g ++ основана на более старом предложении, чем предложение, принятое в C ++ 20. Таким образом, хотя текущий g ++-fconcepts требует concept bool VecInt =
..., C ++ 20 вместо этого потребует concept VecInt =
..., отбрасывая часть bool
. Конечно, тип специализации концепта всегда bool
, поэтому он считался ненужнымтам.)
Это также вызывает еще одно улучшение.Предположим, что вместо одного шаблона функции func
у вас было две перегрузки:
template <VecInt V> std::size_t func(const V&); // #1
template <Vec V> std::size_t func(const V&); // #2
Если вы попытаетесь передать std::vector<double>
в func
, ограничение шаблона # 1 не будет выполнено,поэтому будет использоваться шаблон № 2.Но если вы попытаетесь передать std::vector<int>
на func
, что произойдет?Ответ в том, что при использовании VecInt
вызов неоднозначен, но при использовании моего VecInt
используется шаблон № 1.
В старых шаблонах неограниченных функций C ++ определяет «более специализированный, чем«связь, которая может определять многие случаи того, что« шаблон функции X
может быть вызван с определенным списком типов аргументов, логически подразумевает, что шаблон функции Y
может быть вызван с теми же аргументами ».Более старая логика для этого отношения основана на типах параметров функции.Например, g(std::list<int>&)
более специализирован, чем g(std::list<T>&)
более специализирован, чем g(T&)
более специализирован, чем g(const T&)
.Это помогает C ++ иногда естественным образом «делать то, что я имею в виду», когда существует несколько шаблонов функций с одинаковыми именами.
Как показано в этом примере, иногда удовлетворение одной концепции или ограничения логически подразумевает выполнение другой концепции или ограничения, и этобыло бы хорошо, если бы это означало, что C ++ может использовать этот факт для определения «более специализированного, чем» для перегрузок шаблона функции (и частичной специализации шаблона класса).Но шаблонные ограничения - более сложная вещь, чем типы параметров, и определить их логическое значение, как правило, гораздо сложнее.
Таким образом, C ++ определяет только некоторые довольно простые правила для сравнения ограничений в случаях, когда все ограничения обоих шаблонов удовлетворяются.,Не вдаваясь в точные технические детали, нужно помнить следующие основные моменты:
При сравнении ограничений любое выражение, НЕ являющееся одной из этих четырех вещей, обрабатывается как неизвестное логическое значение:
а.Специализация понятия.
b.Выражение вида E1 && E2
.
c.Выражение вида E1 || E2
.
d.Выражение, которое представляет собой число наборов (
круглых скобок )
вокруг любого из вышеперечисленных.
При сравнении ограничений два выражения, которые не входят в вышеуказанные категории, даже еслинаписанные с одинаковыми жетонами, никогда не считаются эквивалентными.(Но если одно и то же выражение из определения какого-либо одного понятия используется несколькими способами посредством «расширения» определений понятий, это выражение всегда должно рассматриваться как имеющее непротиворечивое логическое значение.)
Если использовать эти упрощенные правила, можно показать (например, используя таблицу истинности), что удовлетворение ограничения X
подразумевает ограничение Y
, также должно быть выполнено, мы говорим, что X
"включает" Y
.И если два ограниченных шаблона функции или частичная специализация шаблона класса будут эквивалентны, а их ограничения игнорируются, а объединенное ограничение шаблона № 1 объединяет объединенное ограничение шаблона № 2 (но не наоборот), это еще один способ получения шаблона.№ 1 считается «более специализированным, чем» шаблон № 2.
Поэтому, сравнивая ваши Vec
и VecInt
, C ++ знает, что Vec<T>
означает is_vector<T>::value
, а VecInt<T>
означает is_vector_of_int<T>::value
, но на этом останавливается и не пытается найти какие-либо логические отношения между этимидва выражения.Таким образом, ни одна концепция не объединяет другую, и ни один шаблон, использующий эти концепции, не является более специализированным, чем другой, что может привести к неоднозначному вызову перегрузки.
При сравнении ваших Vec
и моего VecInt
, C ++ не 'Попробуйте определить, что означает std::is_same_v<typename T::value_type, int>
.Но поскольку Vec<T> && anything
true означает, что Vec<T>
также верно, C ++ знает, что VecInt
включает Vec
, и поэтому func
# 1 более специализирован, чем func
# 2.
хотя концепции и ограничения, безусловно, являются полезным и полезным дополнением к языку, я думаю, что установление связей между понятиями как можно более сильными приведет к различию между концепциями, которые работают достаточно хорошо для конкретных целей, и действительно хорошими концепциями общего назначения для библиотечного уровня.Выполнение последнего будет непростым делом (и абсолютно обязательно потребует общего набора многих базовых стандартных концепций), и я ожидаю, что сообществу C ++ потребуется изучить некоторые правила «наилучшей практики», как делать это вместе по ходу работы.