В этом плане различий нет, имхо, справедливо для обоих смарт-указателей.
Почему он так выразился:
Besides symmetry with make_shared, make_unique offers at least two other advantages.
мне тоже не понятно.
С точки зрения exception safety между unique_ptr и shared_ptr (без использования make_xxx) есть отличие: unique_ptr<T>(p) не бросает исключений, т.е. noexcept; shared_ptr<T>(p) может бросить std::bad_alloc при невозможности выделить дополнительную память в куче для счетчика ссылок и может бросать implementation-defined исключения при других ошибках.
Все плюсуют, значит согласны?
Компилятор вправе выстроить последовательность
1. new int(42)
2. seed()
3. std::shared_ptr()
Исключение в seed дает утечку. Рекомендую Саттера почитать. Он в «Решение сложных задач на С++» в 2.17 пример приводил, хотя для auto_ptr, но сути не меняет.
Почему бы МТС не создать собственный сервис VoIP и не попытаться монетизировать его так как это делает Skype?
Мегафон так уже сделал года 2 как уже. Я в роуминге по миру и по России пользую. Любые звонки по 0.8-1.5 руб./мин., входящие бесплатно, где бы я ни находился. При этом подвязан свой же номер мобильного (абонент на другом конце даже не догадывается, что голос идет по SIP, когда звонит или принимает вызов). Дома использую для междугородних звонков.
Почти все лучшие идеи пришли ко мне не за компом и не в офисе, а в ванной, под душем.
Даже, казалось бы, самые тупиковые проблемы разрешаются сами собой. Идеи валят сами, не нужно даже напрягаться.
Шум воды и осознание того, что никто и ничто не может тебя отвлечь, делают свое дело. Этот кусок времени только твой, даже никто позвонить не сможет.
Порой конечно приходится приходить в себя, понимая, что уже минут 10 трешь себе голову шампунем.
Это полное погружение в себя. Жена не понимает, зачем целый час сидеть под душем.
Да, Вы все верно поняли. Так и есть: в первом варианте ошибка компиляции, во втором варианте выбор будет не то, чтобы случайным, а в зависимости от сортировки типов. Но, как вы правильно заметили, asteroid и space_ship не связаны наследованием, поэтому сортировка типов может их расположить как угодно, можно считать что случайным (implementation specific) образом.
Но это только в примитивном, базовом использовании мультиметода. Есть механизмы, о которых я позже расскажу, которые позволяют вместо неявного формирования списка типов, вручную указать список типов, к которым будут приводиться аргументы. Тогда можно будет ввести вручную тип C и получить ту же ошибку компиляции во втором случае.
Вернее наоборот, более общий класс dispatcher требует ручного ввода списка типов. А multimethod — это фасад к нему, который автоматически этот список формирует.
Я немного не понял Вас. Приведите пример, пожалуйста. Рассматривайте мультиметод так:
сначала каждый аргумент приводится к самому последнему производному типу в иерархии, затем вызывается статически перегруженная функция компилятором по полученным типам аргументов, согласно правилам перегрузки (это ничего, что имена функций в примере разные, они все равно внутри объединяются в один функтор с перегруженным operator() для каждой функции).
Понятно, что не может быть сначала динамический поиск типа, а затем статическая перегрузка. Во время компиляции закомпиливаются все возможные вариации типов аргументов, которые потенциально могут быть преобразованы из входящих базовых типов. То есть во время компиляции строятся двоичное дерево if (cast)-else (можно посмотреть любой псевдокод из приведенных примеров).
Пример про неоднозначность:
struct C : asteroid, space_ship {};
typedef void f1_type(game_object*);
typedef void f2_type(C*);
void f(game_object*);
void f(C*);
game_object* go;
auto mm = make_multimethod((f1_type*)f, (f2_type*)f);
mm(go);
не даст ошибки неоднозначности, т.к. скомпилируется в:
if (C* c = dynamic_cast<C*>(go)) // dynamic_cast способен производить downcast по ромбовидным иерархиям
f(c); // нет неоднозначности, разрешение перегрузки в void f(C*)
else
f(go); // нет неоднозначности, разрешение перегрузки в void f(game_object*);
Представив мультиметод в виде дерева if-else очень просто понять его механизм.
Более усложнив пример, можно добиться неоднозначности, но ее будет просто разрешить, вводя дополнительные целевые функции. Вообще, лучше рассматривать реальные примеры.
Вы чуть-чуть ошиблись. unique_ptr вываливает ошибку компиляции, там где auto_ptr лажал уже в рантайм. Это стало возможно благодаря тому, что unique_ptr может отобрать себе во владение указатель только у xvalue (T&&). Классического копирующего конструктора и operator= он намеренно не имеет, поэтому случайно скопировать unique_ptr не получается на этапе компиляции.
А стандартные алгоритмы теперь явно говорят, что они перемещают значение посредством std::move или делают копию.
Все должно быть в меру. И для каждой конкретной задачи — свой прием. А притягивать всё и вся за уши к ООП только лишь потому, что это должно быть ООП — бессмысленно.
Сколько я видел статических функций внутри классов в Java и C#, даже в стандартных библиотеках, которые там только потому, что по-другому в этих языках никак. Выглядит неестественно.
Есть смысл в превращении функций в функторы — для облегчения встраивания, иначе пришлось бы передавать указатель на функцию. Но все равно с точки зрения дизайна они обычными функциями и остались, если не хранят состояния.
За что люблю C++: есть задача — выбирай подходящий способ решения. В погоне за чистым ООП, теперь может и функциональную парадигму запретить? Лямбды исключить? И связывание аргументов?
Чтобы тело не забыло про нагрузки от сидячки дома, я летом наворачиваю круги на велосипеде по окрестностям Дубны.
Зимой лыжи коньковым ходом по лыжному треку, а до него пешком километр еще пройти. Осенью пешие прогулки.
Самая жесть — это лыжи коньком, отвлекают от всего и бодрят, особенно когда учишься кататься, падая и валяясь в снегу. Возвращаешься еле живой домой. Хотя, как такая нагрузка скажется на сердце, я не знаю.
Еще контрастный душ здорово поднимает настроение.
Прерываясь каждый час на зарядку для глаз, стоя у окна, делаю сразу 2 полезных дела: отрываюсь от работы и спасаю глаза.
Можно сказать, что серьезно не думал, но мечтал. Целиком до буста ей еще расти и расти, в особенности, нужно все жестко формализовать. Но вот внутри есть несколько самостоятельных суб-библиотек, которых в бусте нет, которые я позже опишу. Возможно они бы пригодились там. Это overloaded_function и compressed_tuple
Хотя, народ не дремлет, и, пока моя реализация пылилась несколько лет, в последней версии буста уже опубликовали overloaded_function, даже названия такие же, правда в несколько ином виде.
Там функция полиморфно скрывает свои составляющие компоненты, а у меня, наоборот, открыты для статического встраивания. Можно навесить полиморфную обертку и моя реализация будет идентична бустовой, только по размерам в разы меньше.
boost::overloader_function из-за тривиальной реализации весит n*sizeof(boost::function), n — количество перегрузок. На MSVC boost::overloaded_function перегруженная 5-ю функторами весит 5*16 = 80 байт и имеет солидный оверхед при вызове. Аналогичная mml::overloaded_function весит 1 байт и имеет нулевой оверхед (полностью встраивается). Наверное, нужно сделать сравнительный анализ.
В конечном итоге после выявления реальных типов происходит вызов целевой перегруженной функции по правилам обычной перегрузки языка C++, то есть Вы получите ошибку компиляции с сообщением о неоднозначности.
Спасибо, за вопрос, я об этом забыл упомянуть. Добавлю к статье.
Вы правы, именно из параметров целевых функций, которые передаются в мультиметод, выводятся типы аргументов. Правильный порядок определяется сортировкой типов по предикату is_base_and_derived (он как и остальные параметры задан по-умалчанию, неявно) для каждого аргумента в порядке от роизводного к базовому. Все эти манипуляции происходят во время компиляции. Почти весь код библиотеки является кодом времени компиляции. Производится статическая интроспекция кода с помощью метапрограммирования. Я пока намеренно не пишу о реализации, вводная статья и так получилась раздутой. Если касаться реализации, то писать придется очень много. В либе 57 файлов весом 180 КБ сплошного шаблонного метапрограммирования.
На чистом C, к сожалению, будет гораздо сложнее. Да и на C++ оказалось все очень непросто. Но в C++ удалось сделать многое на шаблонах так, чтобы внешне для пользователя все выглядело привлекательно. Для C мне приходит в голову только 2-мерная матрица указателей на функции + к этому прикрутить полиморфизм, RTTI, регистрировать методы в этой матрице и диспетчить по ней вызовы в зависимости от типа аргументов. Все это геморно с точки зрения и реализации и использования, и выглядеть будет коряво. Лучше уж генератор кода писать
На Obj-C никогда не писал, но слышал, что там вызов метода реализован через диспетчер внутри объекта. Если можно переопределять диспетчер, то с этим можно поиграться.
Если я скажу, что отладочный режим компиляции предназначен не для полноценного соответствия стандарту, а в помощь программисту найти баги до выпуска в продакшн, Вы все равно со мной не согласитесь? Я согласен, что в Вашем конкретном примере отступление от стандарта дало больше вреда, чем пользы. В конкретном случае можно отключить проверку, настроив нужный макрос. Но убирать ее совсем не стоит — уж очень полезная фича.
Почему он так выразился:
мне тоже не понятно.
С точки зрения exception safety между unique_ptr и shared_ptr (без использования make_xxx) есть отличие:
unique_ptr<T>(p)
не бросает исключений, т.е. noexcept;shared_ptr<T>(p)
может бросить std::bad_alloc при невозможности выделить дополнительную память в куче для счетчика ссылок и может бросать implementation-defined исключения при других ошибках.Компилятор вправе выстроить последовательность
1. new int(42)
2. seed()
3. std::shared_ptr()
Исключение в seed дает утечку. Рекомендую Саттера почитать. Он в «Решение сложных задач на С++» в 2.17 пример приводил, хотя для auto_ptr, но сути не меняет.
Здесь нет перегрузки, здесь есть скрытие (hide) метода f базового класса B. Чтобы включить перегрузку, нужно добавить в производный класс using B::f;
Кстати, заявляют, что Интернет-трафик при использовании приложения 0, если в качестве провайдера тот же Мегафон. Хотя не было возможности проверить.
Мегафон так уже сделал года 2 как уже. Я в роуминге по миру и по России пользую. Любые звонки по 0.8-1.5 руб./мин., входящие бесплатно, где бы я ни находился. При этом подвязан свой же номер мобильного (абонент на другом конце даже не догадывается, что голос идет по SIP, когда звонит или принимает вызов). Дома использую для междугородних звонков.
Даже, казалось бы, самые тупиковые проблемы разрешаются сами собой. Идеи валят сами, не нужно даже напрягаться.
Шум воды и осознание того, что никто и ничто не может тебя отвлечь, делают свое дело. Этот кусок времени только твой, даже никто позвонить не сможет.
Порой конечно приходится приходить в себя, понимая, что уже минут 10 трешь себе голову шампунем.
Это полное погружение в себя. Жена не понимает, зачем целый час сидеть под душем.
Но это только в примитивном, базовом использовании мультиметода. Есть механизмы, о которых я позже расскажу, которые позволяют вместо неявного формирования списка типов, вручную указать список типов, к которым будут приводиться аргументы. Тогда можно будет ввести вручную тип C и получить ту же ошибку компиляции во втором случае.
Вернее наоборот, более общий класс dispatcher требует ручного ввода списка типов. А multimethod — это фасад к нему, который автоматически этот список формирует.
сначала каждый аргумент приводится к самому последнему производному типу в иерархии, затем вызывается статически перегруженная функция компилятором по полученным типам аргументов, согласно правилам перегрузки (это ничего, что имена функций в примере разные, они все равно внутри объединяются в один функтор с перегруженным operator() для каждой функции).
Понятно, что не может быть сначала динамический поиск типа, а затем статическая перегрузка. Во время компиляции закомпиливаются все возможные вариации типов аргументов, которые потенциально могут быть преобразованы из входящих базовых типов. То есть во время компиляции строятся двоичное дерево if (cast)-else (можно посмотреть любой псевдокод из приведенных примеров).
Пример про неоднозначность:
не даст ошибки неоднозначности, т.к. скомпилируется в:
Представив мультиметод в виде дерева if-else очень просто понять его механизм.
Более усложнив пример, можно добиться неоднозначности, но ее будет просто разрешить, вводя дополнительные целевые функции. Вообще, лучше рассматривать реальные примеры.
А стандартные алгоритмы теперь явно говорят, что они перемещают значение посредством std::move или делают копию.
Сколько я видел статических функций внутри классов в Java и C#, даже в стандартных библиотеках, которые там только потому, что по-другому в этих языках никак. Выглядит неестественно.
Есть смысл в превращении функций в функторы — для облегчения встраивания, иначе пришлось бы передавать указатель на функцию. Но все равно с точки зрения дизайна они обычными функциями и остались, если не хранят состояния.
За что люблю C++: есть задача — выбирай подходящий способ решения. В погоне за чистым ООП, теперь может и функциональную парадигму запретить? Лямбды исключить? И связывание аргументов?
Зимой лыжи коньковым ходом по лыжному треку, а до него пешком километр еще пройти. Осенью пешие прогулки.
Самая жесть — это лыжи коньком, отвлекают от всего и бодрят, особенно когда учишься кататься, падая и валяясь в снегу. Возвращаешься еле живой домой. Хотя, как такая нагрузка скажется на сердце, я не знаю.
Еще контрастный душ здорово поднимает настроение.
Прерываясь каждый час на зарядку для глаз, стоя у окна, делаю сразу 2 полезных дела: отрываюсь от работы и спасаю глаза.
Хотя, народ не дремлет, и, пока моя реализация пылилась несколько лет, в последней версии буста уже опубликовали overloaded_function, даже названия такие же, правда в несколько ином виде.
Там функция полиморфно скрывает свои составляющие компоненты, а у меня, наоборот, открыты для статического встраивания. Можно навесить полиморфную обертку и моя реализация будет идентична бустовой, только по размерам в разы меньше.
boost::overloader_function из-за тривиальной реализации весит n*sizeof(boost::function), n — количество перегрузок. На MSVC boost::overloaded_function перегруженная 5-ю функторами весит 5*16 = 80 байт и имеет солидный оверхед при вызове. Аналогичная mml::overloaded_function весит 1 байт и имеет нулевой оверхед (полностью встраивается). Наверное, нужно сделать сравнительный анализ.
Спасибо, за вопрос, я об этом забыл упомянуть. Добавлю к статье.
На Obj-C никогда не писал, но слышал, что там вызов метода реализован через диспетчер внутри объекта. Если можно переопределять диспетчер, то с этим можно поиграться.
Например, для считывания номеров с движущихся вагонов для контроля в РЖД.
Похоже, я изначально неудачно выразил свою мысль.