В чем разница между концепциями C ++ 0x и библиотекой проверки концепций Boost (BCCL)? - PullRequest
18 голосов
/ 30 августа 2009

Концепции не сделали стандарт C ++ 0x, но Boost по-прежнему предоставляет Библиотека проверки концепции Boost (BCCL) . Я думаю, что BCCL не охватывает все, что было задумано в стандарте C ++ 0x. В чем разница между BCCL и предлагаемым решением C ++ 0x?

Ответы [ 3 ]

28 голосов
/ 30 августа 2009

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

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

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.

4 голосов
/ 30 августа 2009

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

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

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

3 голосов
/ 19 сентября 2009

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

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

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

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. C ++ 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 () (мы не можем разыменовать целое). С небольшим количеством метапрограммирования шаблона и некоторыми макросами мы можем довольно близко приблизиться к наложению произвольных структурных ограничений на параметры шаблона в удобочитаемом виде . Вот как это может выглядеть при условии соответствующих определений для REQUIRES и RandomAccessIterator:

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

НТН, S

...