Проверка определения шаблона
Большим отличием концепций от этих ручных решений является то, что концепции позволяют проверять определение шаблона, не делая ничего особенного. Библиотека проверки концепта позволяет проверять тип только для использования . Пример:
template<typename InputIterator>
int distance(InputIterator a, InputIterator b)
{ return b - a; }
Теперь вы можете посыпать этот шаблон проверками и признаками концепции, но вы никогда не получите ошибку после написания этого шаблона - потому что Стандарт позволяет компилятору откладывать компиляцию шаблона до момента его создания. Для проверки вы должны написать классы «архетип», которые содержат именно те операции, которые требуются интерфейсу, а затем создать их экземпляры искусственно.
Читая документацию по BCCL, я обнаружил, что она уже включает в себя общие архетипы, такие как "default constructible". Но если вы пишете свои собственные концепции, вы должны будете также предоставить свои собственные архетипы, что непросто (вы должны найти именно ту минимальную функциональность, которую должен обеспечить тип). Например, если ваш архетип содержит operator-
, тогда проверка вашего шаблона с этим (неправильным) архетипом будет успешной, хотя концепции не требуют такого оператора.
Предложение отклоненных концепций автоматически создает для вас архетипы на основе указанных и подразумеваемых требований (например, тип указателя T*
, используемый в параметре, подразумевает требование PointeeType для T
, например). Вам не нужно заботиться об этом - за исключением случаев, когда определение вашего шаблона содержит ошибку типа.
Проверка семантических требований
Рассмотрим этот код, используя гипотетические проверки концепции
template<ForwardIterator I>
void f(I a, I b) {
// loop two times!
loopOverAToB(a, b);
loopOverAToB(a, b);
}
В руководстве BCCL говорится, что семантические требования не проверены. Проверяются только синтаксические требования и типы. Рассмотрим прямой итератор: существует семантическое требование, чтобы вы могли использовать его в многопроходных алгоритмах. Только проверка синтаксиса не сможет проверить это требование (подумайте о том, что произойдет, если потоковый итератор случайно пройдет эту проверку!)
В отклоненном предложении вы должны были явно указать auto
перед определениями концепции, чтобы флаг компилятора успешно работал после проверки синтаксиса. Если auto
не было указано, то тип должен был явно определить концептуальную карту, чтобы сказать, что он поддерживает эту концепцию. Таким образом, потоковый итератор никогда не будет проходить проверку ForwardIterator.
Переопределение синтаксиса
Это была еще одна особенность. Шаблон, такой как
template<InputIterator I>
requires OutputStreamable<I::value_type>
void f(I a, I b) {
while(a != b) std::cout << *a++ << " ";
}
Может использоваться следующим образом, если пользователь предоставит карту концепта, которая учит компилятору, как разыменовать целое число и, таким образом, как целое число удовлетворяет концепции InputIterator.
f(1, 10);
Это преимущество языкового решения, которое, по моему мнению, никогда не может быть решено BCCL.
Перегрузка на основе концепции
При быстром чтении BCCL я также не могу обнаружить ничего, что позволяет этому случиться. Похоже, что ошибка при сопоставлении концепции вызывает серьезную ошибку компиляции. Отклоненное предложение позволяет следующее:
template<ForwardIterator I>
I::difference_type distance(I a, I b) {
I::difference_type d = 0; while(a != b) ++a, ++d;
return d;
}
template<RandomAccessIterator I>
I::difference_type distance(I a, I b) {
return b - a;
}
Если тип можно использовать с обоими шаблонами, то будет использоваться второй шаблон, поскольку он более специализирован: RandomAccessIterator
уточняет концепцию ForwardIterator
.