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

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

Я не полностью понял move semantic (ночь уже). Можно ли с помощью rvalue сделать swap вообще без какого либо копирования? А вообще хорошая статья, прям С++ радует
template <typename T>
void swap(T &first, T &second)
{
    T tmp = std::move(first);
    first = std::move(second);
    second = std::move(tmp);
}


Если тип T поддерживает move-оператор и move-конструктор (что верно, например, для std::vector), то здесь не будет копирования
Вы практически повторили код std::swap для C++11, кстати она переехала из <algorithms> в <utility>.
Это не удивительно, сложно реализовать std::swap как-то иначе :)
можно xor'ами, там тогда даже временная переменная не нужна, правда это только для простых типов подойдёт )
Да хватит уже. Нельзя swap реализовывать xor'ами даже в случае простых типов, т.к. он не будет работать, если оба аргумента будут ссылками на одну и ту же переменную.
Почему? Просвапает ссылки же. Пусть и 2 одинаковые.
Вы не поняли, не указатели, а 2 ссылки на одну переменную.
void swap(int& a, int& b)
{
    a ^= b;
    b ^= a;
    a ^= b;
}
int x = 3;
swap(x, x).
// x == 0
Вы правда думаете, что компилятор будет использовать дополнительную переменную в случае простых типов?
int t = a;
a = b;
b = t;

превращается в
mov eax, [a]
mov edx, [b]
mov [b], eax
mov [a], edx

А вот
a ^= b;
b ^= a;
a ^= b;

Уже в
mov eax, [a]
mov edx, [b]
xor eax, edx
xor edx, eax
xor eax, edx
mov [a], eax
mov [b], edx

Как думаете, что проще и оптимальнее?
А что вы хотели от ответа на риторический вопрос? ;)
Тем более выше шла речь не о том, насколько оптимально его скомпилит, а о том, как записать по другому.
Ну, обычно имеется в виду не просто «иначе» записать, а хотя бы столь же оптимально. Потому что способов просто «иначе» можно придумать сколь угодно много.

Ну, и выше уже сказали, когда XOR-решение не работает корректно.
XOR-решение это вообще из области школьных олимпиад по информатике и вопросов на собеседовании на позицию джуниор С++ программиста: проверяет общее понимание бинарных операций, не вдаваясь в детали и поплёвывая на последствия. В реальной жизни использовать нельзя.
Не всегда можно просто переместить объект в памяти при помощи «лобового» memcpy.
Например, объект может содержать указатели на свои же поля, или указатель на себя.
И тогда при прямом перемещении объект станет испорченным.
Именно поэтому придумали «move semantics» — когда хочется переместить объект, но лобовое перемещение может не сработать.
Поэтому сделать swap при помощи прямых манипуляций с памятью невозможно. Ни на основе memcpy, ни на основе xor и т. д.
Можно сделать swap только на основе move или конструктора копирования.
При чём первый способ быстрее
Когда я в первый раз прочел описание новых фишек, я тоже подумал, что С++ наконец-то радует. Но… нет.
Foreach? Они наконец-то сделали стандартный foreach (правда, вполне в духе С++)? Господа, на дворе 2013 год! Пока все переползут на этот стандартный foreach со своих кривых макросов, пройдет еще лет 10.
Auto? Decltype? Это ложка меда в бочке дегтя под названием «синтаксис STL». Теперь, создавая шаблон каких-нибудь матриц, не нужно будет пару дней выводить тип, возвращаемый операцией умножения. Круто, чё.
Weak_ptr? Они что, шутят? С третьей, или какой там по счету, попытки сделать в STL автоматическое управление памятью, предлагается weak_ptr? Годика через три вопрос «приведите пример, когда нужен weak_ptr» будет встречаться на собеседованиях с такой же частотой, как и вопрос про виртуальное базовое наследование. И с такой же частотой употребляться на практике. Потому что это еще один, тысячный способ гарантированно прострелить себе ногу. А без weak_ptr не будет нормально работать shared_ptr. Ну и зачем тогда всё это?
Лямбды с явно указываемым списком переменных для замыкания? Это всё уже есть с тех времен, когда в плюсах разрешили перегружать operator(). Ну да, экономится строчек пять кода, и на том спасибо.
Самое плохое, что С++ идет куда-то не туда. Страуструп и Ко пытаются догнать современные языки по количеству модных плюшек, имея за спиной тяжеленный груз совместимости. В том же Javascript, при всех его недостатках, не нужен ни дикий синтаксис шаблонов, ни явное указание замыканий, ни тем более шаманства с «умными» указателями, которые подвластны только гениям — компиляторы как-то справляются с выводом всего этого автоматически. А главная фишка плюсов — скорость — при использовании всего этого теряется, потому что все эти универсальные штуки, типа std::map, std::sort или там умных указателей — ровно они же и используются в новых языках.
Как только вы начинаете писать близко к железу и выжимать из него максимум, вы выкидываете весь ооп-мусор, начинаете думать об упаковке данных и в итоге скатываетесь к старому доброму Си, просто с чуть более современным синтаксисом.
Ну и где тут тогда область применения, в которой С++ будет наголову лучше остальных языков?
Не вижу.
weak/shared_ptr в бусте обитает 10 лет и все С++ программисты должны были это знать. Про новые фишки — они не такие уж и новые, просто на разработку стандарты некоммерческой организацией ушло много времени. Можете сравнить с Java там тоже декларировано много чего, а с работающей реализацией все не так радужно
Самое плохое, что С++ идет куда-то не туда. Страуструп и Ко пытаются догнать современные языки по количеству модных плюшек, имея за спиной тяжеленный груз совместимости.


Вы сами и ответили.
Вы не там видите место С++. Нравится Javascipt? Берите Qt, в нём есть прекрасный яваскрипт-подобный QML, есть движок V8 и есть возможность совмещать их гибкость со скоростью и мощью лежащих внизу С++ библиотек. Вьюха на яваскрипте, модель на С++. Хочется быстрее писать — контроллер на яваскрипте, хочется больше контроля над памятью и скоростью — на С++.
На чём написан Хром? ;)
Что же если вы разочаровались в C++ похоже вам дорога на D или Go как сделал Александреску.
Возможно мне бы хотелось чего-либо большего, но я понимаю что «совместимость с С» (по крайней мере с C89) и design by comitee не дадут ничего революционного, хотя бы уже потому, что не сломать существующий код почти невозможно, а создать New C++ который будет несовместим с предыдущими никто не даст. Упомянутый вами JavaScript имеет те же проблемы. Нельзя сломать совместимость и нужно договариваться с другими членами комитета.
Имхо, auto делает код менее читаемым. Мне как-то более удобно читать полное описание итератора в циклах. Этот сахар скорее для удобства написания кода, но код должен быть удобно читаемым, а не удобно пишущимся (это не всегда одно и то же)
Это скорее дело привычки, на самом деле auto как раз удобнее использовать как раз в качестве замены итераторов.
Заметно сокращает код и по-моему повышает его читабельность, конечно при условии понимания предыдущего когда и принципа работы итераторов, как таковых
С итераторами соглашусь — обычно это какой-то типичный контейнер, из контекста обычно понятно, о чем идет речь. Но вот повсеместное использование auto очень мешает анализировать код.

Новый C++, конечно, няша. Увы, из-за использования старых компиляторов, не получается задействовать его плюшки в рабочих проектах, приходится только just4fun проектах.

За статью спасибо! Очень радуюсь, когда кто-то что-то пишет или переводит о C++, сейчас это не очень популярно.
Спасибо!
Вопрос на засыпку: «Почему писать про С++ не очень популярно?»
Я сейчас всего лишь заканчиваю 3 курс, но работаю по специальности, как раз программистом С++ (80% — в Qt). Так вот, когда-то давно, соответственно, вставал вопрос выбора основного языка, начинал изучать Java, но все таки С++ оказался роднее :)
В то же время я мониторил рынок, спрос на те или иные языки (СПб), и С++ ничем не уступал (в то время (2 года назад), да и сейчас вроде тоже) как Java, так и, например, C#. Это касается и количества предложений, и зарплат.
Так вот, что такого в С++ плохого, из-за чего следовало бы прямо сейчас продолжить учить джаву или начинать учить C#?)
Прошу прощения, речь шла не о том, что люди не пишут на C++, а о том, что статьи о C++ на Хабре появляются очень редко. Последняя неделя — так вообще праздник, но обычно все как-то кисло.

В реальности программисты C++ очень ценны и востребованы. Я также, как и Вы, работаю программистом C++ с использованием инструментария Qt. Не жалуюсь :) C++ используется везде и всюду, и если появляется какая-то новая ОС, платформа или что-то еще, с вероятностью 90% она обзаведется поддержкой С++. Так что Ваш выбор правильный.

Ну а C# или Java есть смысл учить для расширения кругозора. Ну и просто некоторые вещи быстрее и лучше сделать на этих языках.
сейчас это не очень популярно.

Это вам так кажется, давайте посмотрим:
Мобильные платформы:
1. Android — усиленно пилят NDK, потому что игры хотят native, дальвик не справляется
2. Tizen/Sailfish/Blackberry/UbuntuPhone — С++ основной язык разработки
3. WindowsPhone — запилили поддержку native (а то есть C++) по многочисленным просьбам
4. iOS в целом тоже позволяет писать на C++, но это не популярно.

Не мобильные платформы:
1. Практически весь софт под Linux написан на C/C++, а учитывая распространение на серверах, это огромное количество кода.
2. Часть софта на Windows, и практически весь кроссплатформенный софт (который не Java)
3. Игры, всякие Doom и прочая, потому что проверенно и шустро
4. Практический любой серьезный софт для рассчетов, систем контроля и т.д.

Ну и список можно долго продолжать,
что бы тут не говорили, С++ до сих пор один из наивостребованейших языков,
как бы не хотелось его смерти всяким неосиляторам.
Как я уже написал в комментарии выше, прошу прощения за свое кривое выражение — я хотел сказать, что статьи на хабре о C++ появляются, увы, очень редко.
Я сам разработчик С++, использую инструментарий Qt, периодически вижу Ваши полезные советы на форуме Qt :) На жизнь не жалуюсь. И полностью согласен с Вашим сообщением про популярность.
Если честно, изначально итераторы тут ни при чем, и появление auto — это лямбды, когда винегретный набор на выходе. Все прочие рассуждения — бальзаковская лирика.

IMHO:
Использование auto / var в обыденных объявлениях лично для меня является генерацией менее человекочитаемого кода, это тоже самое, что использование обратной польской нотации в математических выражениях — вы же ее не используете?
Кроме того, это расхолащивает внимание кодера к используемым типам, и он становится намного более быдлокодером.

Если в шарпе холивар вокруг var в обыденном объявлении имеет место быть в тысячах тредов, то такова архитектура языка, поскольку шарп сам по себе естественнее в чтении, а в сочетании с R# или CodeRush вообще расслабляет.

Но, плюсы итак не просты как шарп, решарпер тут слабый помощник, а 11 делает код еще более замороченным, и еще менее человекочитаемым. Тут бы однозначно поостерегся использовать auto, и рекомендовал бы в код-гайде избегать использования auto везде, кроме лямбды. Лучше сразу щепетильно и дисциплинированно относиться к коду и типизации, чем надеяться на статические анализаторы и огребать на код-ревью.
Что-то мне подсказывает, что с auto может быть ситуация аналогичная var в C#.
Вначале почти все ругаются: да зачем оно надо, кто это будет использовать и тд. Но, со временем, результатом становятся более внятные названия переменных и повсеместный var.
НЛО прилетело и опубликовало эту надпись здесь
Позвольте чуток поправить:
B::f — это просто другой метод с тем же именем, перегруженный, а не переопределенный

И снова это две перегруженные, а не переопределенные функции

Здесь нет перегрузки, здесь есть скрытие (hide) метода f базового класса B. Чтобы включить перегрузку, нужно добавить в производный класс using B::f;
Ну с учетом того, когда появилось предложение такого синтаксиса для лямбд — неизвестно кто на кого становится более похож. В новой яве вон вообще :: стали использовать для «указателей на функции»
Я всегда использовал этой ключевое слово так же и в производных классах (и поощрял людей, кто так делал), чтобы код был понятнее.
И почему же вы тогда поощряете людей использовать дьявольское auto, которое делает код нечитабельным?

// Или, сравним С++03 и С++11
// C++03
for (std::vector<std::map<int, std::string>>::const_iterator it = container.begin(); it != container.end(); ++it)
{
   // do smth
}
// C++11
for (auto it = container.begin(); it != container.end(); ++it)
{
   // do smth
}


Может, так вам станет понятно, что авто зло:


auto container;
for (auto it = container.begin(); it != container.end(); ++it)
{
   // do smth
}

понять, что за контейнер может быть очень проблематично.
auto container;

Это даже не скомпилируется. Вы уверены, что да конца понимаете auto?
На С++11 не писал. Читал фичелист, но забыл про необходимость явной инициализации переменной.
В вашем примере вы делаете код «некомпилируемым», а не «нечитаемым»
ну так нужно назвать его не container а
myFavoritePornSitesArray
или
invertedPageRankMap
и станет понятнее для читающего код, даже с учётом auto.
Каждый раз, когда я вижу на хабре статью с полезной информацией, сразу проматываю в конец. Потому что с вероятностью 90% это просто перевод. А в оригинале читать удобнее и быстрее. Было бы еще лучше, если бы ссылка на оригинал давалась сразу в начале.
Что лично меня забавляет в С++11 — есть make_shared, но нет make_unique. Нет, правда? За много лет работы над стандартном никто не заметил, что они забыли make_unique, и для исправления недостатка им нужен С++14?

А так стандарт очень клёвый. Практически всё, что пишу на С++ — личные проекты for fun, поэтому «переехал» сразу и ни разу не пожалел. Он и правда feels like a new language.
А если не секрет, основной язык какой?
Не совсем забыли.

make_unique имела бы тривиальную реализацию (которая сводится к вызову конструктора unique_ptr).
make_shared же имеет внутри некую «магию», которая выделяет ОДИН кусок памяти под объект и счетчик ссылок вместо ДВУХ кусков памяти, как было бы при вызове конструктора shared_ptr.

Иными словами make_unique полезна (т.к. безопасена при возникновении исключений), но не необходима (реализуется руками за 5 минут).
А вот make_shared необходима, т.к. для её реализации (без применения «чорной магии») надо менять внутренности shared_ptr.
Этот «умный» make_shared после того как выделит память одним куском — вызывает placement new? А как эта штука будет сочетаться с кастомным operator new (в том числе и если кастомный оператор написан конкретно для данного класса)?
Да, он вызывает placement new (по сути — конструктор). С перегруженным оператором new такая штука никак не будет сочетаться. Причина простая: оператор new не отвечает за инициализацию объекта, он отвечает только за выделение памяти, а в данном случае память выделяется «особым» образом. Вообще, перегрузка оператора new в С++ — это плохая идея, которая может приводить к трудно отлавливаемым багам. В stl, boost и т.д. за выделение памяти отвечают аллокаторы. Вместо перегрузки операторов нужно использовать аллокаторы. В частности есть функция allocate_shared, которая делает то же самое, что и make_shared, но выделяет память с помощью указанного аллокатора.

Обратите внимание, в С++ есть 3 механизма выделения памяти:
1. malloc/calloc/free.
2. Операторы new/delete, как глобальные так и классовые.
3. Аллокаторы.

malloc/calloc/free — это седая древность, наследие С, используется только в недрах реализации new/delete. Не вызывают конструкторы, просто выделяют кусок памяти. Использовать их, конечно, можно… только зачем? Единственным исключением здесь может быть calloc, который выделяет память на стеке (это очень быстро) и не имеет аналогов в С++. Память выделенная calloc автоматически освобождается при выходе за область видимости. Calloc можно использовать при особо экстремальных оптимизациях, если точно знаешь что делаешь.

С глобальными операторами new и delete все плохо. Если их переопределить, то малейшая неосторожность в порядке #include может вылиться в несоответствие new и delete. Кроме того, new и delete вносят путаницу просто своим существованием. Дело в том, что их реализация (см. исходники stdlib) отличается от malloc/free только тем, что при ошибке выделения памяти кидается исключение. Вы нигде не увидите явный вызов конструктора, а ведь он вызывается! Но вызывается он «за кадром». Когда вы пишете в коде new X(), язык понимает это как «надо вызвать оператор new для выделения памяти, а потом вызвать конструктор». Сама реализация new при этом ничего не знает про конструктор! То же самое с классовыми операторами. Они ничем не отличаются от глобальных, кроме области «применимости» (С++ сначала ищет классовый оператор, а затем, если он не найден, — использует глобальный). Классовые операторы немного безопаснее глобальных, т.к. delete в большинстве случаев будет вызываться правильный (т.к. он определен в хедере класса; исключение — когда вы вызываете delete для неполного типа: в этом случае не вызовется деструктор (плохо) и вызовется стандартный оператор delete — еще хуже). По итогу: new/delete — это все теже malloc/free, только теперь язык еще вызывает конструктор/деструктор, вызывает сам, операторы про это ничего не знают. Перегружать их опасно, может потом стоить «ноги». Их бы вообще выпилить, но они используются аллокаторами.

С алокаторами все хорогшо :) Аллокаторы — это специальные объекты, которые занимаются выделением и освобождением памяти. Вы можете легко создать несколько независимых аллокаторов, которые никак не будут друг на друга влиять.Если вы знаете, что какой-то алгоритм требует минимума кеш-промахов, вы можете легко выделить всю необходимую ему память с помощью отдельного аллокатора в отдельном физическом адресном пространстве. Стандартные аллокаторы работают внутри через new/delete, однако никто не запрещает им работать через функции операционной системы.

С памятью почти все, не упоминались только delete[] и векторные аллокаторы, но это уже детали. Заметьте, я ничего не написал про placement new. Дело в том, что placement new это не оператор, это даже не функция, он не имеет реализации. Это просто синтаксический механизм, который говорит компилятору, что нужно вызвать конструктор, используя в качестве this такой-то адрес.
Небольшая поправка.
calloc — это malloc + зануление выделенной памяти.
На стеке память выделяет alloca.
согласен, ассоциация выстрелила
Было бы здорово, если бы для каждой фичи указали с какой версии компиляторов её начали поддерживать.
Можно тут глянуть.
Извиние, но то, что тут возможна утечка,
foo(std::shared_ptr<int>(new int(42)), seed());

если seed выбросит исключение, — не правда.
1. Если до вычисления 1 аргумента дело не дошло, то и не дойдёт. Утечки нет.
2. Если сначала будет вычислен умный указатель, то во время исключения в seed будет вызван деструктор этого указателя и объекта. Утечки нет.
Таким образом, исключение в seed никак не повлияет, всё будет нормально.
Утечка возможна только в том случае, если исключение произойдёт в конструкторе shared_ptr.
Кста, есть у мя одна непонятка на эту тему. Вот что пишет один уважаемый человек herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/
Обрати внимание что он пишет нас счет make_unique и exception safety. По идее с shared_ptr<...>(new ..) те же проблемы, но о них саттер не упоминает. Это Саттер забыл упомянуть или все таки между make_shared и make_unique в этом моменте какие то различия есть?
В этом плане различий нет, имхо, справедливо для обоих смарт-указателей.
Почему он так выразился:
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, но сути не меняет.
Посмотрире внимательней на скобки
foo(std::shared_ptr(new int(42)), seed());

Как видите, не может seed() быть вычисленным между двумя этапами инициализации умного указателя.
Оказывается всё же может. В соответствии со стандартом:
1.
If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced. [ Note: The execution of unsequenced
evaluations can overlap. — end note ] Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which. [ Note: Indeterminately sequenced evaluations cannot overlap, but either could be executed first. — end note ]

2.
When calling a function (whether or not the function is inline), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function. [ Note: Value computations and side effects associated with different argument expressions are unsequenced. — end note ]

Так что получается, что компилятор имеет право разделить вычисление выражения аргументов и перемешать их.
Поэтому рекомендуют использовать make_xxx_ptr везде, где это возможно, и не ломать голову насчет потенциальных утечек. Так проще и надежнее.
Да, почитал внимательнее стандарт, там для функций сказано:
Value computations and side effects associated with different argument expressions are unsequenced.

Был уверен, что indeterminately sequenced. Спасибо.
Осознал, что пренебрегаю написанием override в наследниках.
Возник вопрос — насколько целесообразно override'ить методы, переопределяющия абсолютно виртуальные методы?

class B 
{
public:
   virtual void f(int) = 0;
};

class D : public B
{
public:
   virtual void f(int) override {std::cout << "D::f" << std::endl;}
};


Если я ошибусь с типом в перегруженном методе — компилятор все-равно выдаст «Cannot initialize Abstract class», так что override кажется избыточным. С другой стороны, зачем мне сделить абсолютно ли виртуальный у меня базовый метод? Просто взять в привычку всегда писать override в наследниках?
override — хорошая (обратная) проверка с помощью компилятора того, что вы делаете в плане иерархии, наследования и т.д. Хуже не будет, мне лично нравится.
По override Вы получите с каким методом проблема, а по abstract class — не факт (добрая воля компилятора).
К тому же вы можете сменить чисто виртуальный метод на обыкновенный, и эта ошибка уже может ускользнуть.
Ключевое слово virtual опционально и поэтому немного затрудняло чтение кода, заставляя вечно возвращаться в вершину иерархии наследования, чтобы посмотреть объявлен ли виртуальным тот или иной метод
Любая приличная IDE выделяет виртуальные методы, независимо от использования ключевого слова virtual в наследниках.
Я не знаток приличных IDE для С++, не подскажете пару штук? Visual Studio, насколько я помню, не умеет такого.
Студия начиная с 2008 насколько я помню такое делала по дефолту. Честно говоря давно ей не пользуюсь, в основном QtCreator.
Eclipse+CDT выделяет.
Qt Creator.
VisualAssist для VisualStudio, тоже позволяет выделить.
НЛО прилетело и опубликовало эту надпись здесь
Поправьте меня, если я не прав, но такие константы стоит объявлять статическими, а запись
class Foo
{
  static const int myConst = 0xB00B5;
};
была возможна и в С++03.
Немного поправлю себя же. Такая запись допустима только для простых (или даже интегральных, не помню точно) типов. А в новом стандарте можно и объекты так инициализировать.
НЛО прилетело и опубликовало эту надпись здесь
А вот для неконстантных членов — это действительно приятная возможность избежать дублирования умолчательного значения в ворохе конструкторов.
НЛО прилетело и опубликовало эту надпись здесь
В чём смысл? В списке? Ну, для новичка в C++ или C++11 — новые приятные штучки. Без «страшных» деталей.
В последнем листинге, кстати, было выяснено (но не известно точно почему), что:
assert(this != &temp); // assert if this is not a temporary
не имеет смысла, т.к. this все равно может оказаться temp, а проверить это нет возможности.
Необходимо самому сделать следующее:
if(this != &temp)
{
  // 1. освобождаем ресурсы, занятые этим объектом
  _buffer = nullptr;
  // 2. берем данные из temp
  _size = temp._size;
  _buffer = std::move(temp._buffer);
  _name = std::move(temp._name);
  // 3. присванивание чего-либо temp будет препятствовать тому, чтобы деструктор
  // освободил ресурсы, ранее занимаемые этим объектом
  // temp._buffer = nullptr; <-- нет необходимости из-за использования std::move
  temp._size = 0; // <-- в этом, соответственно, тоже нет необходимости
}
return *this;
Можно ли при объявлении типа функции как auto объявить рекурсивную функцию, например факториал?
auto fact(long long n)
{
    return (n == 1)? 1: n * fact(n - 1);
}

Я думаю вполне должно работать, хотя надо проверить. При объявлении функции типа auto, компилятор будет искать нужный тип в возвращаемом значении функции, умножение этому помещать не должно. Хотя, если будут проблемы, то, возможно, понадобится как-то прикрутить decltype
Да, конечно же, надо использовать decltype, следующий код рабочий:
auto fact(long long n) -> decltype(n)
{
    return (n == 1)? 1: n * fact(n - 1);
}
Будет в С++14.
В комментариях выше, подкинули идеи использовать swap, оператор присваивания копии в последнем примере можно переписать следующим образом:
Buffer& operator=(Buffer copy)
{
     swap(*this, copy);
     return *this;
}
// ...
void swap(Buffer& first, Buffer& second) noexcept
{
     using std::swap;
     swap(first._name  , second._name);
     swap(first._size  , second._size);
     swap(first._buffer, second._buffer);
}
Статья интересная, спасибо, но я бы упомянул, что половина «возможностей С++11» — это не возможности вовсе, а расширения стандартной библиотеки.
В Википедии всё категоризовано — изменения в языке и в библиотеке.
Стандартная библиотека — это как бы часть языка.
Библиотека на то и библиотека, что её использование опционально. И если кто-то хочет использовать собственные шаблоны, алгоритмы и так далее, то и «умные» указатели ему придётся изобретать самому, а автоматические переменные или, допустим, строго-типизированные перечисления никуда не денутся.

upd: ну, а вообще забейте, я не собирался никому читать лекции. Просто заголовок бросился в глаза :)
Расширение стандартной библиотеки так же является и расширением языка, разве нет? Другое дело, когда меняется какой-то синтаксис или семантика или же добавляются принципиально новые функции/возможности — по этим признакам классифицировать можно
В C++ очень мало «батареек» в стандартной библиотеке. И практически вся стандартная библиотека — всего лишь поддерживает те или иные механизмы именно языка. Поэтому, например, стандарт языка «знает» про utf-8, но стандартной функции по преобразованию utf8-строки в u32string или u16string и наоборот — нет.

В частности, почти все изменения библиотеки в С++11 отражают изменения в самом языке: variadic templates, constexpr, auto и т.п.
P.S. А ещё эту ситуацию можно трактовать так, что возможности языка улучшали, что бы можно было пользоваться ими с помощью стандартной библиотеки.
Тока код поправь

std::map<std::string, std::vector<int>> map;
for(auto it = begin(map); it != end(map); ++it) 
{
   // do smth
}


должно быть

std::map<std::string, std::vector<int>> map;
for(auto it = map.begin(); it != map.end(); ++it) 
{
   // do smth
}
Нет, в коде все правильно, здесь используются новые non-member функции (из 8 пункта) — std::begin и std::end
извиняюсь, дочитал не до конца.
А в чем они отличаются от одноимённых методов? Плюсы есть какие-нибудь?
Одинаково работают для контейнеров и для массивов.
Кроме того, что работают для обычных массивов, их можно переопределить для какого-нибудь класса, не меняя его, например, если класс принадлежит библиотеке, поменять код которой проблематично.
В примере про лямбда-выражения ошибку при компиляции примера предлагается решить заменой левой части выражения с auto на std::function. А на самом деле можно сделать понятнее:

auto fib = [&fib](int n) -> int {return n < 2 ? 1 : fib(n-1) + fib(n-2);};


Да, синтаксис непривычный, но в таком виде можно явным образом определить тип возвращаемого лямбдой значения. Во многих случаях сильно экономит время и нервы.

Кстати, важное дополнение: компилятор не всегда будет выводить тип возвращаемого значения лямбда-функции по левой части выражения даже тогда, когда она задана в явном виде. Например, компилятор не выведет его, если внутри лямбды есть несколько точек возврата с условным оператором и разными типами.
Мне кажется, стоить добавить, что auto поддерживает модификаторы.

Примеры:
const auto &rc = MyFunc1();
auto *p = MyFunc2();

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


Что вам мешало привести еще подобный пример?

#include <iostream>

enum class __enum : int { one = 0 };

int main () {
    std::cout << static_cast<int>(class __enum::one) << std::endl;
    
    return 0;
}


Почему нет ни слова про union и final в таком контексте?

#include <iostream>
 
union __union final {
    const int i;
};
 
int main ()  {
    using foo = const union ::__union;
    foo un = { .i =  0 };
 
    std::cout << un.i << std::endl;
 
    return 0; 
}


Также вы наверное забыли про спецификаторы delete, default:

struct  __struct final {
    __struct (const value&&) = default;
    __struct (const value&) = delete;
};


А как же constexpr?

#include <iostream>
 
constexpr int foo() {
    return 1;
}
 
int main() {
    int array[foo() + 1] = {0, 1};    
 
    std::cout << std::boolalpha << (array[0] < array[1]) << std::endl;
 
    return 0;
}


Неужели было так сложно привести реализацию std::make_unique?

namespace std {
	template <typename __type, typename ...__arguments>
		inline std::unique_ptr<__type> make_unique_helper (std::false_type, __arguments &&...arguments)
		{
		  return std::unique_ptr<__type>(new __type(std::forward<__arguments>(arguments)...));
		}
	
	template <typename __type, typename ...__arguments>
		inline std::unique_ptr<__type> make_unique_helper (std::true_type, __arguments &&...arguments)
		{
			using unique = typename std::remove_extent<__type>::type;
	
			return std::unique_ptr<__type>(new unique[sizeof...(__arguments)] {
				std::forward<__arguments>(arguments)...
			});
		}
	
	template <typename __type, typename ...__arguments>
		inline std::unique_ptr<__type> make_unique (__arguments &&...arguments) {
			return make_unique_helper<__type>(std::is_array<__type>(),
				std::forward<__arguments>(arguments)...);
		}
}


Почему ни слова про std::move_if_noexcept, std::forward, declval.

Раз уж подняли тему про лямбда-выражения почему не упомянули о том что std::unary_function, std::binary_function и пр. то что в С++11 deprecated.

template <typename T>
struct find_equal : std::binary_function <T, T, bool>
{
	find_equal(const T &value) : value(value) {};

	bool operator()(const T &left, const T &right) const {
		return left == value && right == value;
	}

	private:
		T value;
};

file.erase(std::unique(file.begin(), file.end(), ::find_equal<char>(' ')), file.end());



Теперь вместо такого нагромождения можно писать так:

file.erase(std::unique(file.begin(), file.end(), [](const char &left, const char &right) {
     return left == value && right == value;
}), file.end());


:)
Я надеюсь вы понимаете, что и это не все возможности С++11? Я в самом начале статьи упомянул, что
существует много новых дополнений к языку и стандартной библиотеке, эта статья лишь поверхностно охватывает часть из них.
Обо всех возможностях можно книгу писать, в статью уж точно не уместится
Я понимаю что речь шла только о десяти нововведениях, но почему не описать их более подробно и качественно?
Просто таким макаром можно и в википедии прочитать обзор.

Не поймите меня не правильно, но я лишь хочу чтобы технические статьи содержали не только красивые загловки, но хороший материал от которого можно будет потом отталкнуться в работе.
* Опечатался в первом примере, указал лишний спецификатор class должно быть так: __enum::one
enum class __enum: int { one = 0 };
union __union final {
struct __struct final {
template <typename __type, typename ...__arguments>


Хозяйке на заметку:
Each name that contains a double underscore _ _ or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.

[global.names]/1.

Чтобы поучать других в таком тоне, нужно научиться не допускать ошибок самому.
Ага, дурная привычка (изучение исходников STL дает о себе знать).
Функция, объявленная как final, не может быть переопределена функцией F::f() — в этом случае, она переопределяет метод базового класса (В) для класса D.

Не скомпилится. И это хорошо. Потому что иначе б смысла от final не было.
Я пока только учусь писать на C++, и сразу возникла мысль, возможно глупая. Может ли auto избавить от написания перегруженных функций?
Нет. Избавить от написания перегруженных функций иногда могут шаблоны.
В качестве аргумента функции нельзя указать переменную типа auto.
На мой взгляд auto — очень не аккуратная вещь.
Его рекомендуют использовать только при объявлении лямбды.

Гораздо проще сделать хедэр в котором написано
///...
typedef unsigned char            T_1b;          /* 1 byte unsigned */
typedef unsigned short           T_2b;          /* 2 byte unsigned */
typedef unsigned int                T_4b;          /* 4 byte unsigned */
typedef unsigned long long      T_8b;          /* 8 byte unsigned */

и т.д.
Заменять, например, unsigned long long на auto никто и не советовал, размер кода это сократит, а вот читаемости поубавит, typedef'ами в таких случаях, по-моему, пользуются из покон веков. В Qt, к примеру, об этом изначально позаботились, и ранее обозначенный тип записывается как quint64.
auto — это, в первую очередь, для работы с итераторами (ну и с лямбдами, разумеется)
В вашем же примере:
auto i = 42;        // i - int
auto l = 42LL;      // l - long long(!)
auto p = new foo(); // p - foo*

Жто скорее в качестве примера, для наглядности.
Почему не аккуратная? Кто рекомендует?
Тот же Herb Sutter советует use auto wherever possible. А дядька знает, о чем говорит.
Такой хедэр уже есть, называется cstdint.
Да, я слышал о чём-то подобном, но всё-равно использую свой. Удобно. Типы короткие (4 символа) и понятная логика названий.
Логика как раз отсутствует. Просто Вы привыкли. Вот как можно догадаться, что означает T_1b? При том, что для обозначения типа в C/C++ принято _t (size_t, ptrdiff_t, ...). Далее, «беззнаковость» типа из названия также совершенно неочевидна.
У меня логика есть. У меня тип без знаковый если не написано иначе(знаковые S_1b и т.п.).
T_1b означает Type 1 Byte
P. S. Давайте не будем спорить о вкусах.
Давайте не будем спорить о вкусах

О вкусах и не думал спорить. Я ведь сказал выше о привычке.

Просто стандарт — это общепринятое, даже если для кого-то было это неочевидно, то после знакомства это становится очевидным. А вот увидев в коде T_1b или S_1b я буду дестабилизирован.
В догонку весьма любопытная статья на счет того, почему С++ не имеет будущего. 70% аргументов чистый троллинг, однако оставшиеся 30 вполне правомерны dlang.ru/10-prichin-pochemu-u-cpp-net-budushhego
Минорная ошибка в #10 — rvalue в С++98/03 — то от чего нельзя взять адрес. Хороший контрпример то что является rvalue, но имеет имя «this».
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации