Comments 42
В начале декабря прошлого годя я обращался в ДМК Пресс, где издавался перевод предыдущих книг Майерса, по поводу издания новой книги. Мне ответили, что лицензия куплена, и через несколько месяцев выйдет перевод книги. Так что скоро будет и на русском.
+4
Лучше по чаще себя пинать в плане изучения английского. Для программера знание английского достаточно выгодный навык
+3
Спасибо за совет, но английским я владею. Проблема в том, что я люблю читать аналоговые книги и не люблю платить за 336 страниц 44$ или почти 3000 рублей (текущая цена без доставки на Amazon). А еще большая проблема в том, что ни ваш, ни этот мой комментарии не имеют никакого отношения к топику.
+2
Из всех книг по программированию, которые я пытался читать в переводе на русский, буквально несколько переводов были действительны хороши. В остальных либо куча ошибок, либо очень тяжело читается, потому что переводчики не смогли сохранить стиль автора и текст превратился в какое-то нагромождение терминов.
+3
Регистрируетесь на сайте Oreilly и пользуетесь кодом на электронную книгу 50% для всех мемберов.
Могу и поделиться, пишите в личку. Ценю Oreilly за DRM.
Могу и поделиться, пишите в личку. Ценю Oreilly за DRM.
-1
Ну и русский тоже можно освежить в памяти ;)
0
Ну как минимум рядом с override должен быть еще и final.
+3
В книге рассказываются нововведения и в C++14, например, auto в параметрах лямбда. Хотя и вы начали аннотацию с " От С++98 к С++11/14. Галопом по всем", про C++14 Вы не писали.
PS отличная идея рассказать о такой книге!
PS отличная идея рассказать о такой книге!
+2
Более того, по сути выражение std::iterator_traits<It>::value_type — не более чем гениальный костыль, придуманный на заре STL для определения типа получающегося при разыменовании итератора, первый вариант будет работать только с типом для которого определена специализация iterator_traits<>, а вот для второго нужен лишь operator*().Немного неточно. Во-первых, при разыменовании итератора получается ссылочный тип (
T&
), а value_type
— это тип с отброшенной ссылкой (T
). Во-вторых, у std::iterator_traits<It>
есть реализация по умолчанию, в которой std::iterator_traits<It>::value_type
равен It::value_type
. А нужен этот вспомогательный класс затем, что у обычного указателя (T*
) не может члена-типа данных value_type
, поэтому стандартная библиотека предоставляет для указателей специализацию данного класса.Эта коллизия может произойти с любым кодом уже сейчас, по правилам С++11Этот пример вроде неплохо демонстрирует, почему оно так работает. Проблема инициализации через фигурные скобочки в том, что она делает два дела одновременно: предоставляет синтаксис для инициализации с помощью
std::vector(10, 20) создает обьект из 10 элементов, тогда как
std::vector{10, 20} создает обьект только из двух элементов.
std::initializer_list
и работает как универсальная инициализация с контролем над сужением, которая может быть использована в любом месте, где синтаксисом языка допускается инициализация.В C++98 для создания такой конструкции MyAllocList пришлось бы обьявить шаблонной структурой, продекларировать тип внутри нее и использовать вот так:Надо учитывать, что такой подход дает большую гибкость, потому что шаблон черезMyAllocList<Widget>::type lw;
using
нельзя специализировать. Если брать пример с iterator_traits
, то мы сможем написать так:template<typename It>
using iterator_value_type = It::value_type;
но не сможем потом его специализировать, чтобы он работал и для обычных указателей.default — этот модификатор заставляет компилятор генерировать автоматические функции классаТут есть один неприятный момент: если компилятор не cможет эту функцию сгенерировать, то он не выдаст вам сообщение об ошибке, а просто втихушку не станет её генерировать. Скажем вот такой код отлично компилируется:
struct T {
std::unique_ptr<int> p; // unique_ptr не имеет конструктора копирования
T(const T&) = default; // поэтому компилятор не может сгенерировать этот конструктор для T
};
а ошибку вы получите, только когда попытаетесь данный конструктор использовать.Однако один нюанс все-таки существует — decltype возвращает ссылку для всех выражений отличных от просто имениЭто неправда, например:
std::string f();
decltype( f() ) s; // s будет иметь тип std::string
decltype
работает так: если ей передать имя переменной или выражение доступа к члену данных (например, a->b.c
) без скобочек, то тип будет совпадать с типом переменной или члена данных. Иначе это считается выражением, а любое выражение имеет свой нессылочный тип (назовем его T
) и value category (не знаю, как это правильно перевести). В соответствии с value category добавляется ссылочность: для lvalue результат будет T&
, для xvalue — T&&
, для prvalue — T
.+11
>>delete — призвано заменить старый (и красивый) трюк с приватным обьявлением конструктора по умолчанию и оператора присвоения
Я правильно понимаю, что авторе рекомендует теперь вместо boost::noncopyable использовать способ с delete?
Я правильно понимаю, что авторе рекомендует теперь вместо boost::noncopyable использовать способ с delete?
0
Да, совершенно верно, boost::noncopyable это просто надстройка над приватным конструктором.
+1
По желанию, на мой взгляд. Внутри noncopyable, boost использует тот же самый delete, если собирать с поддержкой C++11:
#if !defined(BOOST_NO_CXX11_DELETED_FUNCTIONS)
noncopyable( const noncopyable& ) = delete;
noncopyable& operator=( const noncopyable& ) = delete;
#else
private: // emphasize the following members are private
noncopyable( const noncopyable& );
noncopyable& operator=( const noncopyable& );
#endif
0
Просто не могу определиться по этому вопросу. Есть вопрос нна stackoverflow и ответ весьма двояк. С одной стороны да, для новичка delete будет понятен, но для большинства программеров, для которых boost как «отче наш» вполне возможно будет noncopyable понятнее
0
Вопрос действительно неоднозначный, исходя из того что boost продвигал новый функционал из TR1 подручными средствами (и слава ему за это), а у стандарта руки развязаны, у них должно получиться лучше. Технически оба варианта работают одинаково, вопрос только в выразительности.
Я вообще против канонизации библиотек, boost проделал огромную работу и все его экспериментальные разработки вошли в стандарт, пусть теперь дальше целину пашут. Лично я стараюсь везде где можно использовать std:: вместо boost::
Dura standard sed standard
Я вообще против канонизации библиотек, boost проделал огромную работу и все его экспериментальные разработки вошли в стандарт, пусть теперь дальше целину пашут. Лично я стараюсь везде где можно использовать std:: вместо boost::
Dura standard sed standard
0
Эх, к сожалению, многим прграммистам, работающим в больших корпоративных проектах, еще долго не светит не только С++14, но и С++11. Так что остается только облизываться.
0
Так никто же не мешает завести свой небольшой проект на C++11/14 или поддерживать уже существующие.
+2
Ну если бы кто-нибудь мне заплатил, я бы и завел, и поддержал. :)
0
тогда облизывайтесь бесплатно на здоровье
+8
Инвестиции в себя. Вам заплатят (ваш работодатель, новый работодатель, заказчик и т.п.), после того, как вы докажите что владеете определёнными технологиями, показав, например активный профиль на гитхабе. Да и кроме того, новые технологии это же просто интересно (по крайней мере для меня).
0
UFO just landed and posted this here
В этом случае точный тип derefUPLess известен только компилятору, его просто невозможно сохранить в переменной не используя auto. Конечно возможно написать так:Если лямбда ничего не захватывает (квадратные скобочки пустые), то её можно преобразовать в указатель на функцию:
bool (*derefUPLess)(const std::unique_ptr<Widget>&, const std::unique_ptr<Widget>&) =
[](const std::unique_ptr<Widget>& p1, const std::unique_ptr<Widget>& p2)
{ return *p1 < *p2; };
Выход простой, если в классе Widget перемещающий оператор присвоения продекларирован как noexcept, обьекты будут перемещаться, если нет — копироваться.А если у Widget перемещающий конструктор не
noexcept
, но копирующего конструктора нет, то все равно перемещаться. Эту функциональность реализует std::move_if_noexcept()+1
unless copy constructor is not available, in which case move constructor is used either way and the strong exception guarantee may be waived
Очень сомнительное место. Да, gcc-4.8 работает именно так, но вектор портится напрочь. Я бы так писать никому не советовал, интересно как авторы стандарта это мотивируют?
код для поиграться
#include <atomic>
#include <vector>
#include <iostream>
#include <stdexcept>
struct A
{
A() : i(++cnt) {}
A(const A&) =delete;
A(A&& x) : i(x.i) {
std::cerr<<"A("<<i<<") is moved at "<<cnt<<"\n";
x.i=0;
if(++cnt > 7)
throw cnt;
}
int key() const { return i; }
static int cnt;
int i;
};
int A::cnt=0;
main()
{
std::vector<A> v;
std::cout<<v.capacity()<<std::endl;
for(int i=0; i < 5; ++i) {
try {
v.push_back(A{});
} catch(int x){ std::cerr<<"bang#"<<x; std::cerr<<std::endl; }
std::cout<<v.size()<<": ";
for(const auto& a : v) std::cerr<<a.key()<<" "; std::cerr<<std::endl;
}
return 0;
}
0
но вектор портится напрочьНу уж не напрочь, вектор останется старой длины, просто часть его элементов будет смувлена. C ним даже работать дальше можно (добавлять/удалять элементы и т.д.). Хотя стандарт ничего этого не гарантирует:
Otherwise, if an exception is thrown by the move constructor of a non-CopyInsertable
T
, the effects are unspecified.
интересно как авторы стандарта это мотивируют?А что делать то предлагаете, если копирующего конструктора нету, а перемещающий не
noexcept
? Ошибку компиляции выдавать?0
Вектор не портится конечно, технически он остается той же длины, но элементы вектора перемещающим оператором переведены в какое-то неопределенное состояние, технически наверное валидное, но логически совершенно бессмысленное. Представьте себе что элементы вектора — виджеты связанные отношениями родитель-дитё, имеюшие размер, другие параметры, и вдруг часть из них превращается в неинициализированные обьекты, такие какими их возвращает дефолтный конструктор? Что на экране-то появится?
Естественно ошибку! Копировать не можем, мувать я считаю в такой ситуации тоже не имеем права, значит ошибка.
Ошибку компиляции выдавать?
Естественно ошибку! Копировать не можем, мувать я считаю в такой ситуации тоже не имеем права, значит ошибка.
0
У вас же ещё и исключение наверх выбросилось, если вы его поймав, спокойно продолжаете с массивом работать, то это ваши проблемы. Главное, что вектор будет находиться в состоянии, достаточном для вызова его деструктора (хотя стандарт даже этого не гарантирует). Всё-таки гораздо чаще нужно не strong exception safety, а просто кинуть исключение, чтобы оно там где-то снизу по стеку поймалось. Плюс, если исключение кидается при добавлении одного элемента в конец массива и не происходит реаллокация, то мы вполне можем откатиться в предыдущее состоянии (хотя, опять же, стандарт не говорит, что это произойдет), а на этапе компиляции этого не выяснить.
Ну и выдачу ошибки компиляции в этом случае можно самому прикрутить к вектору строчек в 20 кода, а наоборот было бы сделать проблематично.
Ну и выдачу ошибки компиляции в этом случае можно самому прикрутить к вектору строчек в 20 кода, а наоборот было бы сделать проблематично.
0
если вы его поймав, спокойно продолжаете с массивом работать, то это ваши проблемы
Как так? Исключение для того и ловится чтобы продолжать работать, вся раскрутка стека именно для этого и придумана. Если нужны только исключения которые завершают работу, так это abort() называется.
Проблема с вектором именно в том что исключение может выбросить какой-то из средних элементов, когда часть уже перемещена в новый буфер, а часть еще осталась на старом месте.
По-моему, хватит уже на эту тему, все равно этот кусочек явно не додумали. Вот если можете прислать ссылку на то место где об этом прямо в стандарте говорится, буду благодарен. Хочу на этом месте закладку иметь.
0
наконец-то стандарт признал что существующая в С++98 спецификация исключений неэффективна, признал ее использование нежелательным (deprecated)
А вот это кто-нибудь может пояснить? Типа исключения выбрасывать теперь неправильно? Или о чём это?
0
Вот, нашёл с ходу: en.wikibooks.org/wiki/C++_Programming/Exception_Handling#Exception_specifications
Речь не о самих исключениях, а об указании, что функция может выбросить только некоторый набор конкретных исключений (или не выбросить вообще — именно это и оставили, изменив синтаксис и поведение).
По описанию очень мутная фича. Ни разу не встречал в реальном коде.
Речь не о самих исключениях, а об указании, что функция может выбросить только некоторый набор конкретных исключений (или не выбросить вообще — именно это и оставили, изменив синтаксис и поведение).
По описанию очень мутная фича. Ни разу не встречал в реальном коде.
+3
Ничего не имею против книги, но «C++» и «Modern» в одном предложении?
-8
UFO just landed and posted this here
Тогда уж стоит ещё упомянуть, что помимо спецификатора
Такая встроенная примитивная compile-time проверка на то, может при вычислении выражения броситься исключение или нет. Можно использовать в сочетании со спецификатором
noexcept
, есть ещё и compile-time оператор c тем же именем, который принимает на вход выражение и возвращает false, если в составе этого выражения есть хоть один вызов функции без noexcept
спецификатора, и true в противном случае (более подробно о правилах можно прочитать здесь):void f() noexcept;
void g();
cout << noexcept(f()); // true
cout << noexcept(g()); // false
cout << noexcept(true ? f() : g()); // also false
Такая встроенная примитивная compile-time проверка на то, может при вычислении выражения броситься исключение или нет. Можно использовать в сочетании со спецификатором
noexcept
:void use_f() noexcept( noexcept(f()) ) // noexcept(true)
void use_g() noexcept( noexcept(g()) ) // noexcept(false)
0
Sign up to leave a comment.
Аннотация к «Effective Modern C++» Скотта Майерса