Как стать автором
Обновить

Комментарии 30

Занятно. Посмотрел Copy Constructible — внутри по сути определение нового класса, а так как класс не используется, то будет выкинут на этапе линковки. Хорошая технология.
Это мастхев фича. В языках новой волны требования к параметрам обобщений стараются делать сразу в основе языка.
Например в Rust это сделано через трейты/типажи.
В языке Ceylon более многословно, но и достаточно читабельно, через специальное ключевое слово. В последнем есть также поддержка ковариантности и контравариантности через ключевые слова out и in.
Об этом(как-то «ограничения на типы» или что-то в этом роде), кстати, Страуструп писал в «Дизайн и эволюция C++». И это в 90тые годы — не помню, какие доводы были за и против.
Кому интересно — раздел 15.4 «Ограничения на аргументы шаблонов»
Полезная фича, вывод от ошибки в шаблоне может быть великоват.
Насколько я понял, в стандарт не вошли только сами концепты. Вот, к примеру, is_copy_constructible помечен как «C++11». Пример по ссылке gcc 4.8.2 не собирается, поскольку
  /// is_trivially_constructible (still unimplemented)
Но без строк содержащих is_trivially_constructible работает.
Да ладно, неужели VS не показывает откуда растут ноги у ошибки? gcc про любые ошибки пишет к какой строке пользовательского кода она относится:
/usr/include/c++/4.8/bits/stl_algo.h: In instantiation of ‘void std::sort(_RAIter, _RAIter) [with _RAIter = std::_List_iterator<int>]’:
test.cpp:7:45:   required from here
/usr/include/c++/4.8/bits/stl_algo.h:5452:22: error: no match for ‘operator-’ (operand types are ‘std::_List_iterator<int>’ and ‘std::_List_iterator<int>’)
     std::__lg(__last - __first) * 2);
Хотя конечно такая ручная проверка — полезная штука.
Сейчас все это неплохо реализуется с помощью static_assert и type-traits, имеющихся в стандартной библиотеке либо самописных. Boost, видимо, использует какие-то ухищрения, чтобы не требовать C++11?
Если я правильно понял, type-traits не про то. Тут не нужно проверять типы, тут нужно проверять, обладает ли класс какой-то функций. Хотя возможно type-traits и это умеет, но вроде нет.
Я имею в виду вспомогательные шаблоны классов типа std::is_copy_constructible. В стандарте они называются type-traits, по сути так же отражают фундаментальные концепты.
en.cppreference.com/w/cpp/types/is_copy_constructible
Concepts отражают скорее не обладание набором функций, а обладание неким базовым поведением. Это может быть и набор каких-то функций (как для random_access_iterator), и просто поведение (например is_nothrow_move_constructible).
<not-a-cpp-developer>Не является ли ситуация, когда вам нужно сказать переменному типу, что он должен реализовать определенный интерфейс, той самой ситуацией когда как раз интерфейс и надо использовать?</not-a-cpp-developer>
Бывает полиморфизм времени исполнения и времени компиляции. Программирование с использованием шаблонов это как раз второй вариант, и он дает широкие возможности для статической типизации и оптимизации результирующего кода.
Не всегда нужно и возможно применить наследование, чтобы выделить общий интерфейс. Можете считать это утиной типизацией.
Спасибо за ответ, тогда еще вопрос, эту проблему нельзя решить на уровне языка? Ведь можно было бы добавить сахар вида
template <class T implements RandomAccessibleIterator>
либо даже что-то вроде
actslike вместо implements чтобы избежать стат. связывания
Ответил ниже, но не успел отредактировать съеденный тег. Выглядеть будет примерно как Вы себе представляете.
Связывание у шаблонов именно статическое, они позволяют использовать статическую типизацию на полную катушку.
Возможно Вы путаете generics в Java/C# и шаблоны C++. Первые, вроде, как раз и реализованы на динамическом связывании.
Под «избежать стат связывания» я имел ввиду конечного класса, который попадет в аргумент тайп-переменной, с интерфейсом :) То есть компатибилити-чек опять же произойдет в момент компиляции, когда компилятор наткнется на инстанциирование нового generic-класса (сори за использование термина «generic», но я не знаю как его надо было бы назвать на языке плюсов), он не будет смотреть имплементировал ли данный класс данный интерфейс, а будет просто проверять соответствует ли данный класс данному интерфейсу (можно назвать это неявной реализацией).

Еще раз спасибо за ответ, кстати соседняя статья про CRTP тоже проливает немного света на эти вопросы.
Комитет планирует внести данные изменения в стандарт, условно называемый C++17. Ориентировочно использование выглядеть будет так:
template void sort (C& c);

Список фундаментальных концептов формируется и собирается уже сейчас и доступен например на cppreference.com, пока без поддержки со стороны языка.
Нанотех, безусловно, интересный. Однако, глядя на вспомогательный класс и на исходную ф-ию, для которой он был написан, заметно, что вспомогательного кода гораздо больше и он сложнее. Можно сослаться на надуманность примера. Но по-моему, чем сложнее будет испытуемый шаблонный код, тем сложнее и объемнее будет вспомогательный код для проверки концепций.
Всегда поражался отсутствию конструкции interface в C++.
НЛО прилетело и опубликовало эту надпись здесь
Ну так если бы в шаблонах можно было указывать интерфейс класса T, это решило бы проблему, правда?
Нет, поскольку это привело бы нас к требованию классу T быть наследником некоторого интерфейса, а это другой тип полиморфизма, т.е. по сути то, ради ухода от чего шаблоны и придумывались. К примеру, мы хотим взять из одной библиотеки класс комплексного числа, а из другой — какой-то хитрый контейнер, в который мы эти числа будем складывать. Если класс комплексного числа удовлетворяет требованиям к элементу контейнера (конструктор копирования, оператор сравнения) — мы можем это сделать. Но если класс контейнера вдруг начнёт требовать чтобы его элементы обязательно наследовались от какого-то интерфейса IElement — мы уже не можем положить в этот контейнер наше комплексное число из другой библиотеки, поскольку оно, конечно, ничего об этом интерфейсе не знает.
А чем виртуальные классы лучше? То же самое, комплексное число из другой библиотеки может не наследовать этот виртуальный класс.
Ничем не лучше, поэтому и существуют концепции Boost, о которых и статья.
Эээ нет, извините.

Допустим, у нас заявлен некоторый интерфейс.
И, допустим, мы научили компилятор понимать, что некоторый класс соответствует этому интерфейсу даже если в сигнатуре класса *не* написано implements Interface.

Всё, проблема решена. Не вижу принципиальных препятствий для этого.
С абстрактными классами этот подход не проходит, если он содержит хоть один неабстрактный метод.
Допустим, у нас заявлен некоторый интерфейс.
И, допустим, мы научили компилятор понимать, что некоторый класс соответствует этому интерфейсу даже если в сигнатуре класса *не* написано implements Interface.

Всё, проблема решена.


Да, решена. И это будет называться Concepts, активно пилится и, я надеюсь, скоро войдет в стандарт.

Не скажите. Интерфейс можно добавить к новому классу, даже если он унаследован уже от кого-то. И потом, их может быть несколько. Работает аналогично. Объвляешь переменную именем интерфейса, и не важно обьект какого класса в ней находится, главное, что в нем реализован этот интерфейс.
Однако те, кто пользовался шаблонами C++ знают, на что могут быть похожи сообщения об ошибках. На что угодно, только не на подсказку
Отлично сказано!
Раньше думал, что темплейты это круто, а дженерики в .Net это ужас, потому что не позволяют написать код в стиле
T Sum<T>(T a, T b) where T : operator +
{
   return a + b;
}

но когда я вижу, что в С++ только появляется то, что в .Net есть с версии 2.0 десятилетней давности… Не-е-ет, лучше уж то, что имеем :)

Надеюсь на постепенное слияние идей дженериков (обобщений) .Net и темплейтов (шаблонов) C++: с одной стороны, поддержка ограничений на этапе компиляции с человеческими сообщениями об ошибках, с другой — возможность сделать обобщение не только на определенные методы, но и на операторы: >, < +, — и пр.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации