Подтвердить что ты не робот

Какая разница между концепциями С++ 0x и контрольной библиотекой Boost Concept (BCCL)?

Понятия не сделали стандарт С++ 0x, но Boost по-прежнему предоставляет The Boost Concept Check Library (BCCL). Я предполагаю, что BCCL не охватывает все, что предназначалось для использования в стандарте С++ 0x. В чем разница между BCCL и предлагаемым решением С++ 0x?

4b9b3361

Ответ 1

Проверка определения шаблона

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

template<typename InputIterator>
int distance(InputIterator a, InputIterator b) 
{ return b - a; }

Теперь вы можете посыпать этот шаблон концептуальными проверками и чертами, но после создания этого шаблона вы никогда не получите ошибку, поскольку стандарт позволяет компилятору откладывать компиляцию шаблона до создания экземпляра. Для проверки вам нужно написать классы "архетипа", которые содержат именно те операции, которые требуются интерфейсу, и затем создавать их искусственно.

Читая документацию BCCL, я обнаружил, что он уже включает в себя общие архетипы типа "по умолчанию". Но если вы напишете свои собственные концепции, вам также придется предоставить свои собственные архетипы, что нелегко (вам нужно найти именно ту минимальную функциональность, которую должен предоставить тип). Например, если ваш архетип содержит 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.

Ответ 2

Концепция С++ 0x представляет собой функцию основного языка, весь процесс которой будет выполняться компилятором.

Библиотека проверки уровня Boost - это почти такая же функция, но написанная на С++ и макрос в виде библиотеки, чтобы имитировать некоторые функции. Он не может делать все, что потребуется в конечной языковой функции (в зависимости от определения окончательной функции), но предоставляет некоторые эквивалентные решения для проверки типа шаблона (и другие проверки времени компиляции).

Как было предложено, поскольку концепция С++ 0x - это языковые функции, она позволила бы обеспечить более элегантную семантику и позволить компилятору использовать информацию, недоступную в настоящее время для программы, позволяя более подробные или интеллектуальные ошибки во время компиляции (как первая цель концепции - разрешить проверку абстрактного типа в шаблонах).

Ответ 3

Отказ от ответственности: я не смог успешно использовать BCCL за последние 30 минут, хотя я уже установил Boost. Пример, который вы видите ниже, выглядит в порядке в соответствии с документацией BCCL Boost 1.37, но не работает. Я думаю, это считается недостатком.

С BCCL вы получаете только что-то вроде статических утверждений, тогда как функция концепции основного языка обеспечивает полную модульную проверку типов и способна предотвратить использование некоторого шаблона функции в разрешении перегрузки. С помощью собственных понятий тело ограниченного шаблона может быть немедленно проверено компилятором, тогда как BCCL не делает компилятор ничего проверять в этом отношении. Вам необходимо вручную создать шаблон с параметрами типа "арх", чтобы увидеть, использует ли шаблон какие-либо операции, которые недоступны (например, оператор - на итераторах вперед).

Что касается разрешения перегрузки, вот пример:

template<typename Iter>
void foo(Iter,Iter) {
   BOOST_CONCEPT_ASSERT((RandomAccessIterator<Iter>));
}

void foo(long, int);

int main() {
   foo(2,3); // compile-time error
}

Шаблон является лучшим совпадением, потому что для не-шаблона требуется преобразование из int в long. Но инстанцирование терпит неудачу, потому что int не является итератором. Вы получите хорошее сообщение об ошибке, объясняющее это, но это не очень приятно, не так ли? С помощью собственных понятий вы могли бы написать

template<typename Iter>
  requires RandomAccessIterator<Iter>
void foo(Iter,Iter) {}

void foo(long, int);

int main() {
   foo(2,3); // OK, picks non-template foo
}

Здесь шаблон функции не будет принимать участия в разрешении перегрузки, потому что требование для T = int не выполняется. Отлично!

Мы по-прежнему получаем ограничение шаблонов функций с помощью трюка SFINAE. С++ 0x расширяет SFINAE до выражений и вместе с аргументами аргумента decltype и default для шаблонов функций мы можем написать

template<typename T> T&& make();

template<typename Iter, class = decltype( *make<Iter>() )>
void foo(Iter,Iter) {}

void foo(long, int);

int main() {
   foo(2,3); // OK, picks non-template foo
}

В этом случае вывод аргумента шаблона будет терпеть неудачу, потому что компилятор не знает, каким должен быть тип выражения * make <Iter> () (мы не можем разыменовывать int). С небольшим количеством метапрограммирования шаблона и некоторыми макросами мы можем приблизиться к наложению произвольных структурных ограничений на параметры шаблона читаемым способом. Вот как это выглядит, если принять соответствующие определения для REQUIRES и RandomAccessIterator:

template <typename Iter
  REQUIRES( RandomAccessIterator<Iter> )
>
void foo(Iter,Iter) {}

НТН, S