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

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

Ооо! Последователи Александреску :-) Его книжка и библиотека просто показательны в магии шаблонов. Вот только жаль не все фичи шаблонов поддерживаются компиляторами.
У Александреску на мой взгляд сложно изложено. Большинство современных актуальных компиляторов ( vs2005, intel, gcc, xcode ) частичную специализацию поддерживают, хотя есть нюансы.
xcode и vs2005 — IDE, а не компиляторы ;)
я думаю все это знают :). Подрозумевается Microsoft Visual C++ Compiler 8.0+ и GCC 4.0+ with
Си тут ни при чем. Тег поправьте, пожалуйста.
готово. отсутствие на хабре '+' ввело меня в заблуждение :)
Отличная статья. Отчасти материал частичной специализации шаблонов также рассматривал Саттер. У него хорошим, понятным языком изложено, рекомендую :)
А не уточните, в каком из его трудов? У меня сейчас на полке exceptional, more exceptional и его совместное с Александреску про coding standards. Что-то я на память у него простого изложения не помню. По крайней мере о том, что частичная специализация — это новый класс я узнал значительно после прочтения трудов Саттера и был очень неприятно удивлен такому «открытию». Зато сразу понял еще часть stl и boost :).
В русском варианте — это «Новые сложные задачи на С++», в главе про пример Димова-Абрамса (со специализацией функций) он рассмотрел частично этот вопрос.

Вообще, про новый класс — это в принципе логически понятно. Ведь по сути — шаблон, это еще не класс как таковой. Не инстанциированный шаблон — вообще не компилируется. Шаблон — это указания компилятору правил по созданию классов во время компиляции. А специализация, частичная специализация, не просто указывает компилятору на то, что нужно сгенерировать код для нового класса/фунции, но и создает новый уровень индирекции при поиске, к примеру, вариантов для перегрузки.
меня всегда поражало, что в С++ при должном умении с помощь перегрузки операторов и шаблонной магии можно эмулировать достаточно сложные концепции, свойственные другим языкам… Смотришь на какой-нибудь boost::proto и думаешь даааа! чего только не делают люди лишь бы не писать на Haskell =)

Тут есть нюанс. В С++ можно не только эмулировать, но и заточить под специфические нужды. В более высокоуровневых языках части приходится довольствоваться только тем, что есть :). Тут вопрос в ресурсах и трубованиях — для каждого языка есть области, для которых он «заточен» и там уделывает все живое.
Так вот во всех холливарах постоянно всплывает один и тот же вопрос: а не проще ли взять уже заточенный язык?

Т.е. я понимаю можно бить в бубны и совершать ритуальные пляски с мощью шаблонов, а можно скажем взять наговнякать парсер для DSLа и сгенерировать скажем С-шный код.

P.S. Я ничего не имею против С++, но сильно надеюсь, что писать или поддерживать приложения на нём (а не на его подможестве aka С with classes) мне никогда не придётся =) Слишком громоздко. Just a matter of taste.

Мое мнение — зависит от задачи. Для одних задач лучше один язык + фреймворк, для других — другой. Лично я в разных проектах использую c, c++, c#, java, python и php. По моему опыту — если «не промахнуться» с выбором языка и технологии вообще — то все будет сухо и комфортно. Ну а если промахнуться и попытаться kernel mode windows driver писать на haskell… тут наверное даже бубны не помогут :)
Я вас абсолютно поддерживаю. Промахнутся и воспользоваться C++ exception в оном драйвере наверное тоже будет не очень приятно =)

Мне вот как компиляторщику всегда было интересно, что может дать тот или иной язык при написании собственно компилятора или VM. Почему-то тот же Hotspot (а это ведь фактически rocket science на ниве compiler construction) написан именно в стиле С-with-classes, без особой метамагии…

Хочется поэтому понять, где же метамагия действительно по делу?

Постараюсь через пару недель раскрыть тему про сиськи метамагию. Делегаты, события, invertion of control framework, event driver programming, actor model.
Спасибо. Было бы вообще здорово если бы вы при этом учли «соперников» (например, специализированные языки, либо просто языки, в которых такие фичи уже есть) и показали, почему С++ их делает на некоторой конкретной задаче и по каким параметрам =)

А тут беда — C++ в этой области их вообще не делает :). Максимум что я могу — это показать «как сделать то же самое на c# более просто и в пять раз меньше строк кода» :). Такая магия обычно используется, когда очень хочется применять высокоуровневые абстракции ( делегаты, сообщения ) в языке, который для этого не совсем предназначен но который мы обязаны использовать. Некоторые проекты требуют С++, нельзя ни смешать технологии, ни использовать другие. А хорошо жить хочется. Приходится колдовать.
Я не прав, в том, что применив boost мы получаем все эти радости жизни, разве только без gc, коя неизвестно радость или горесть :)
Ага, получаем радости и к ним лес граблей и кучу трудночитаемого кода.

Все-таки каждой области — свой инструмент, цпп хорош для тех нечастых случаев когда нужен кусок очень высокопроизводительного кода (хотя тру аскеты пользуют plain C, и в этом есть немало здравого смысла, но мне в си темплейтов не хватает %). Ну или фреймворк не хочется с собой таскать (shareware).
Ну нечитаемость кода вещь довольно субъективная :)

Я не супер-программист, но ещё ни разу я не сталкивался чтобы c++ ограничивал меня в чём то, точнее когда я натыкаюсь на это, то это скорее всего кривизна архитектуры.

Вот что реально бесит, так это сообщения об ошибках в духе

In member function ‘boost::tuples::tuple<cvm::basic_scmatrix<double, std::complex>, cvm::basic_cvector<double, std::complex>, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type, boost::tuples::null_type> Structure::dispeq(int, double)’:

Ну и когда подойтёт C++0x, с его концептами, автотипами и прочими радостями, буудет вообще класс.
Естессна принципиальных ограничений у цпп нету, можно написать что угодно (как и на машине тьюринга, хехе).

Проблема в том что он слишком сложный (-> труднее найти/обучить программера), содержит кучу неочевидных заподлянок, заставляет думать о низкоуровневых проблемах там, где без этого вполне можно было бы обойтись. Это все делает разработку на цпп более дорогой и менее эффективной.

Ну и многие интересные и полезные (реально!) вещи типа замыканий и лямбд на нем хоть и можно как-то реализовать, но пользоваться ими потом очень неудобно.

Основная сильная сторона — это мощные оптимизирующие компиляторы, и неплохие средства compile-time программирования (шаблоны). Поэтому я например, хоть и заработал большую часть бабла в своей жизни программированием на C++, сейчас его использую только там где скорость выполнения очень важна — для остального есть сишарп, ява и питон, и времени они мне экономят кучу.

(надо заметить, у шароварщиков есть дополнительные причины использовать цпп, но это не по моей теме).

еще это достаточно простой способ низкоуровневой интеграции в операционную систему: виртуальное оборудование, сервисы, расширение и изменение функциональности ( тот же punto switcher или radmin ). Отсутствие необходимости разворачивать framework для конечных пользователей.
Кривизна архитектуры приложения я имел ввиду.
Он по многим корпоративным стандартам не проходит. Кто-то не хочет держать у себя много егабайт кода в котором каждый месяц находят много багов ( для сертификации, например, критично ). Кто-то по стандартам не может использовать исключения. А так я конечно обеими руками за буст, штука очень хорошая.
Ооо сиплюсплюс эксепшены в драйвере (под wince) это было вообще супер! Неделя отладки, незабываемый опыт по изучению внутренностей винды и плюсового CRT!

Хотя результат конечно можно было и так предсказать :)
что же вы с компилятором сделали :). Они же обычно отказываются С++ код в драйвер компилить — там бинарный формат исполняемого файла немного другой да и вообще О_О.
Под winCE там драйвер это просто dll (это, видимо, и подкупило автора того кода :), так что с компилятором ничо делать не надо.
> Почему-то тот же Hotspot (а это ведь фактически rocket science на
> ниве compiler construction) написан именно в стиле С-with-classes, без особой метамагии…

Потому что те, кто его писал, не умеют писать на С++. Во многих местах метамагию можно было бы применить довольно по делу.
Есть также мнение, что метамагия не применялась по религиозным (политическим) соображениям.
;)
Почему-то сразу вспомнились разные метакомпиляторы, используемые поверх C++. С ними функционал языка кажется просто безграничным. Начиная с bison/flex для реализации других языков и заканчивая метаобъектным компилятором Qt.
согласен, сам qt для некоторых проектов использую, очень удобно. Но зависит от задачи — иногда проще написать нужное на шабллоне, чем тянуть метакомпилятор и соответствующие техпроцессы.
Было где-то, что если знаешь c++ то изучить другие языки легко, только вот зачем :)

Сам занимаюсь матмоделированием, начинал на MATLAB, пробовал python, но всё таки остановился на c++ чем очень доволен.Всякие дикие извраты с шаблонами использую :)
На первой картинке ошибка:

>> A---> void A::foo(int)

благодарствую, исправил
спасибо, классная статья
спасибо. тема действительно сложная и хорошей литературы по ней маловато.
Статья полезная и действительно в книгах много чего не пишут ))) А это живой опыт и практика!
Если будут продолжения с удовольствием буду читать :-)
да, все будет. Меня очень интересует реакция на более сложные вещи, а это так сказать подготовка к ним — дабы можно было из последующих статей ссылаться сюда в виде «как получить тип возврата функции — см. сюда» :)
> … не очень хорошо раскрыты в популярных книгах по си.
Не совсем понимаю причем тут си. Опечатка?
не очень хотелось писать длинное «си плюс плюс» или второй раз «c++». Переделал.
Спасибо автору. Я сейчас сам начал сильно углубляться в C++. Вот сижу пишу игрушку. :)
P.S: если кому-то интересна игрушка «точки», то можете посмотреть: https://sourceforge.net/projects/gdots
P.S.S: есть только сорцы, и те без вменяемых тулз для конфигурации. Если кто-то хочет, то попробуйте собрать так, как есть. У многих собралось.
Спасибо, пошли действительно полезные статьи по c++
minifix: «параметры — это то, что после template в фигурных скобках» там не фигурные а угловые скобки
спасибо, исправил
Раз автор задел тему шаблонов, советую написать вам еще тему о том почему специализации шаблонов функций не перегружаются и как это протеворечит интуитивным ожиданиям со стороны разработчика.)
Уточните, пожалуйста. Там 20 страниц стандарта, я в детали особо не вдавался — специализация функций применяется достаточно редко :(. Вот такой код в VS2005 компилируется и работает корректно:

#include <windows.h>
#include // основной шаблон
templatevoid foo( T ) {}

// специализированый шаблон
template< typename S, typename U > void foo( std::map< S, U > ) {}

// перегрузка основного шаблона
templatevoid foo( T, int ) {}

// перегрузка специализированного шаблона
template< typename S, typename U > void foo( std::map< S, U >, int ) {}

void main( void )
{
foo( 10 );
std::map< int, char > oMap;
foo( oMap );
foo( 10, 11 );
foo( oMap, 11 );
}

Вы какой случай имели в виду?
У Саттера есть пример Димова-Абрамса:

template
void f(T);

template
void f(T*);

template
void f(int*)



int *p;
f(p); // Что вызывается??

А теперь фокус… меняем обьявления местами:

template
void f(T);

template
void f(int*)

template
void f(T*);

int *p;
f(p); // Что вызывается?? То же самое? Отнюдь!

Вот про это я говорю, можно написать статейку)
Хабра поела синтаксис… сейчас исправлю!(не могу чаще 5 минут постить)
из того что я понял по съеденному синтаксису — валидное поведение, вы используете механизм «угадывания» аргументов шаблона по аргументам функции. Алгоритм угадывания зависит от того, что было написано перед определением. Если не использовать угадывание а вручную прописать от чего специализируем: fили f< int* > — то все работает однозначно.
У Саттера есть пример Димова-Абрамса:

template <class T>
void f(T);

template<class T>
void f(T*);

template<>
void f(int*)



int *p;
f(p); // Что вызывается??

А теперь фокус… меняем обьявления местами:

template<class T>
void f(T);

template<>
void f(int*)

template<class T>
void f(T*);

int *p;
f(p); // Что вызывается?? То же самое? Отнюдь!

Вот про это я говорю, можно написать статейку)

PS. Я бы не называл механизм выведения — угадыванием..)
ЗЫ. Текущая статья была названа «трюки» хотя самих трюков в ней не йоты, посему я решил что этот нюанс тоже можно было бы осветить… ваше право)
Усе правильно.
template<> foid f( int* ) 
автоматически угадывает аргумент шаблона по аргументу функции. Так как до ее объявления у нас уже была специализация
template<class T> void f(T*);
то в соответствии с логикой угадывания ( 15 страниц мелким шрифтм стандарта ^_^ ) компилятор угадал вот так:
template<> foid f< int >( int* ) 
. Во втором слуае во время угадывания получилось
template<> foid f< int* >( int* ) 
. Все честно.

А приоритеты вызова еще на 5 страницах стандарта, «как компилятор узнает какая из специализированных шаблонных функций более специализированная» :).

Я подумаю как это добавить в статью. Оно просто малопреминимо, не хотелось бы засорять избыточной информацией.
В стандарте нет мелкого шрифта) Только курсив, жирности и моноширный, для всего свой конекст…
Мало людей учит язык по стандарту, и далеко не много прибегают к нему при разрешение трудных ситуаций (хотя это самое правильное дело), потому что даже стандарт отмалчивается в многих спорных моментах. Да и чтение стандарта имеет малый КПД.

ЗЫЫ… Он выводит)) выводит))) to deduce != to guess )))
Слово угадывать асоциируется с недетерминированностью, компилятор же все выведения делает строго по правилам
Полностью вас поддерживаю — многие вещи в стандарте описаны достаточно сумбурно, что я пометил в топике и что послужило одной из причин его написания. Что же до слова «угадывать» — к сожалению, частая преподавательская деятельность заставила меня приучиться использовать упрощенные термины, дабы за терминологией не терять смысл. Мне сейчас «чисто конкретно по научному и в когнитивной синергии со специализированной терминологией, свойственной для майоритарной совокупности парадигм вышеназваной предметной области» достаточно тяжело писать :). Но я думаю что это ведь не главное? ^_^
Сугубосубъективное мое мнение: учить студентов \ школьников что компилятор угадывает) это не очень хорошо)
Прочтение книги Дракона дает полное понятие о том что и как «угадывает» компилятор)))
Согласен, не очень хорошо. Но когда есть два варианта — человек поверхностно и не совсем глубоко поймет как работает
template<> foo( int )
или корректно и в правильных терминах не поймет — приходится применять первый вариант. А если будет нужно или человеку самому будет интересно — можно потом расширить имеющиеся знания рассказав что угадывание на самом деле подчиняется сложным правилам. Но это все понятное дело не silver bullet — просто я так привык делать и переучиваться уже поздно :).
P.S. Есть еще третий вариант — человеку в правильных терминах и полностью корректно рассказали и он все понял. Но это либо человек шибко умный должен быть, либо очень хороший преподаватель. Я так не могу :(.
статья порадовала. благодарю автора!
> Конечно, при высокоуровневом программировании использовать такие фокусы череповато — думаю, все помнят конструкцию для получения размера массива :)

Можно про «конструкцию для получения размера массива» подробнее?
Подробнее расписано, например, здесь
Если не ошибаюсь, то именно такая конструкция использована у Майкрософт — она называется _countof().
Картинки умерли (
Увы, это очень старая статья, тогда Хабр еще не умел перезаливать на gabrastorage. Я у себя не смог найти оригиналы :( Если кто покажет где сохранилась старая версия — без проблем перезалью
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации