Комментарии 54
C++ Concurrency in Action книга замечательная, есть в русском переводе: Параллельное программирование на C++ в действии. Могу сказать одно, что обязательна к прочтению.
Про Concurrency: недавно делал сравнение реализацию потоков в винде. и был удивлен. По сигнатуре потоки и иже с ними в STL взяты из boost, однако по скорости они проигрывают существенно.
Ух ты, я и не догадался, что есть std::enable_shared_from_this. Написал свой велосипед (довольно кривой). Теперь выпилю велосипед, переделаю на std :)
Чем больше читаю эту книгу, тем больше прихожу в ужас от C++: еще больше неоднозначности и перегруженности в словах, еще больше «тонких» моментов, которые надо помнить, чтобы не допустить ошибку. Просто поваренная книга по отстреливанию ноги.
Эм? Что вас смущеат, по-моему все поведение логично и очевидно, если знать принцип построения языка.
Я понимаю, что всё логично, при расширении стандартов надо не поломать обратную совместимость и придерживаться определённого стиля.
Простой пример: move-семантика. Если бы мы разрабатывали C++ с нуля, разве стали бы мы придумывать std::move, std::forward, и синтаксический ужас вроде int&& myvar? Я не эксперт в написании компиляторов, но, мне кажется, это можно было бы реализовать на автоматическом уровне.
И таких вещей много, просто полистайте книжку, она состоит из «вопреки нашим ожиданиям компилятор делае так», «этот особый случай надо иметь ввиду».
Простой пример: move-семантика. Если бы мы разрабатывали C++ с нуля, разве стали бы мы придумывать std::move, std::forward, и синтаксический ужас вроде int&& myvar? Я не эксперт в написании компиляторов, но, мне кажется, это можно было бы реализовать на автоматическом уровне.
И таких вещей много, просто полистайте книжку, она состоит из «вопреки нашим ожиданиям компилятор делае так», «этот особый случай надо иметь ввиду».
НЛО прилетело и опубликовало эту надпись здесь
Справедливости ради стоит заметить, что очевидное поведение на то и очевидное, что должно быть понятно без всяких принципов.
Вот вам очевидно, что когда на тело не действует сила, то оно движется равномерно или находится в покое. Потому что вам известен курс элементарной физики и вы решили много задач по теме. А вот Аристотелю — нет.
Допустим мы изобрели язык, который очевиден каждому программисту, но я уверен, что найдется специалист по праславянскому, для которого это будет билиберда.
Это я к чему, к тому, что очевидность — это не абсолютное понятие, а относительное.
Допустим мы изобрели язык, который очевиден каждому программисту, но я уверен, что найдется специалист по праславянскому, для которого это будет билиберда.
Это я к чему, к тому, что очевидность — это не абсолютное понятие, а относительное.
Вот вам очевидно, что когда на тело не действует сила, то оно движется равномерно или находится в покое. Потому что вам известен курс элементарной физики и вы решили много задач по теме. А вот Аристотелю — нет.
Я бы назвал это логичным, но ни как не очевидным. Например, так ли очевидно, что в искривленных пространствах на тело, на которое не действует никакая сила, будет оставаться в покое или двигаться равномерно? А если пространство не просто выпукло/вогнуто (если это можно так назвать), а скручено? Где-то даже читал, что наше пространство может быть таким.
Искривление пространства тождественно действию силы гравитации. Поэтому в таком пространстве оно будет идти не прямолинейно и равномерно, а по отображению прямой в этом искривленном пространстве.
Это также очевидно, но уже из нас троих (Аристотель, прости) только мне, т.к. я знаю принципы и идеи построения ОТО.
Это также очевидно, но уже из нас троих (Аристотель, прости) только мне, т.к. я знаю принципы и идеи построения ОТО.
Ну вот мы опять пришли к тому, с чего начали. Может быть очевидно, если имеются специальные знания, знания каких-то принципов. Так можно что угодно считать очевидным.
Тут конечно появляется граница, что считать специальными знаниями, а что общими. Я думаю так — если до чего-то можно додуматься, просто наблюдая мир вокруг себя, не прилагая целенаправленных усилий (т.е. не строить специально большиегудронныеадронные коллайдеры и подобное) — то можно считать такие знания общими, а выводы, полученные на их основе — очевидными.
Тут конечно появляется граница, что считать специальными знаниями, а что общими. Я думаю так — если до чего-то можно додуматься, просто наблюдая мир вокруг себя, не прилагая целенаправленных усилий (т.е. не строить специально большие
Меня, как программиста на C, смущает type&&.
Символов им в ASCII не хватает или чего? '$' не использован еще, зато на бедный амперсанд вешают уже пятую функцию: побитовый AND (if (attributes & EFI_FILE_ATTRIB_FIXED) {...}), логический AND (if (a && b) {...}), взятие адреса (UINT8* ptr = (UINT8*) &a;), ссылочный тип (void f(const QByteArray& data) {...}) и теперь вот еще один ссылочный тип. С нетерпением жду новых стандартов, уже интересно, куда еще амперсанд допишут.
Символов им в ASCII не хватает или чего? '$' не использован еще, зато на бедный амперсанд вешают уже пятую функцию: побитовый AND (if (attributes & EFI_FILE_ATTRIB_FIXED) {...}), логический AND (if (a && b) {...}), взятие адреса (UINT8* ptr = (UINT8*) &a;), ссылочный тип (void f(const QByteArray& data) {...}) и теперь вот еще один ссылочный тип. С нетерпением жду новых стандартов, уже интересно, куда еще амперсанд допишут.
Доллар же вроде можно как идентификатор использовать, по крайней мере такое в gcc компилируется:
Ну и справедливости ради, rvalue ссылка должна бы быть похожей на lvalue ссылку, а первые три функции в вашем списке пришли из C.
int main()
{
int $ = 5;
}
Если память не изменяет, даже видел, как доллар использовали как имя макроса в какой-то C библиотеке.Ну и справедливости ради, rvalue ссылка должна бы быть похожей на lvalue ссылку, а первые три функции в вашем списке пришли из C.
Изначально в Си зарезервировано не так много символов и слов. Любое их расширение это возможность что-то кому-то сломать. Поэтому если логика позволяет в туже зарезервированную базу ввести еще что-то, то надо делать.
Первые три пришли из Си, ссылка — это разименованный указатель, поэтому логично использовать амперсант (звездочка в типе уже задействована), ну а двойной амперсант по аналогии с указателем на указатель.
Первые три пришли из Си, ссылка — это разименованный указатель, поэтому логично использовать амперсант (звездочка в типе уже задействована), ну а двойной амперсант по аналогии с указателем на указатель.
ну а двойной амперсант по аналогии с указателем на указательНу это не совсем корректная аналогия, это же не ссылка на ссылку. И потом, три звездочки можно, а три амперсанда уже нет.
В C++11 появилась такая штука, как reference collapse. То есть, использование && для r-value ссылок оказалось довольно логичным решением.
По моему опыту, C++ можно использовать на своём уровне знаний и опыта и не пользоваться при этом тем, в чём не уверен. И при этом решить почти любую задачу. Ну как минимум ту, которую тебе на твоём уровне поставят. И всё будет быстро, надёжно и без UB.
lvalue определить сравнительно просто: это все что может стоять слева от знака присвоения '='.Определение так себе, во-первых, это верно лишь для встроенного оператор присваивания, но не для перегруженного:
int f();
f() = 0; // Ошибка, в rvalue нельзя присвоить
struct T {};
T g();
g() = T{}; // Нет ошибки, хотя g() - rvalue
Во-вторых, это не объясняет, откуда это понятие вообще взялось и зачем оно нужно. Суть тут в следующем: в стандарте С++ при описании механизма вычисления выражений ссылочность не используется: «If an expression initially has the type "reference to
T
", the type is adjusted to T
prior to any further analysis», то есть тип выражения всегда нессылочный, ссылки просто откидываются. А раз так, то нужен какой-то другой способ различать, является ли результат выражения адресом объекта указанного типа или же самим этим объектом (значением). Вот этой роли в стандарте и служат понятия lvalue/rvalue. Если представить себе, что все встроенные операторы — это тоже вызовы каких-то функций, то если соответствующая функция возвращала бы T&
— это выражение lvalue типа T
, возвращала бы T&&
— xvalue типа T
, возвращала бы просто T
— prvalue типа T
. Объяснение, конечно, немного с конца к началу, т.к. в обычном C никаких ссылок не было, а понятия lvalue/rvalue были (и служили той же цели), и ссылки в C++ были введены, как способ вернуть lvalue из функции, но для C++ программиста так должно быть понятнее имхо.Также можно заметить, что в моем перечислении есть какое-то prvalue, а просто rvalue нету. Действительно, в C++11 базовых категорий три — это lvalue, xvalue и prvalue, и выражение может быть ровно одним из этого списка (до C++11 rvalue ссылок не было, поэтому категория xvalue отсутствовала, а prvalue называлась просто rvalue). Вместе с тем есть ещё два обобщенных понятия: glvalue = lvalue + xvalue и rvalue = xvalue + prvalue. Соотвественно, разделение glvalue/prvalue в С++11 выражает то самое классическое разделение «адрес объекта/значение», а вот само понятие rvalue выражает другую идею — идею перемещения. Основное свойство rvalue — им в отличии от lvalue может быть инициализирована rvalue ссылка (
T&&
), тем самым из результата такого выражения возможно перемещение.На самом деле все stl контейнеры хранят указатели на внутренние данныеКроме
std::array
Хочу добавить ссылочку о lvalue/rvalue: The lvalue/rvalue metaphor:
A simple interpretation of the mysterious value categories of C++: lvalues represent objects and rvalues represent values.
В терминах C++11 ваша цитата неверна, об этом есть где-то ближе к концу моего предыдущего комментария. Суть в том, что есть три базовых категории и все попытки рассуждать о них, как о двух (как, например, в вашем тексте), на мой взгляд, некорректны и обречены на определенное словесное жонглирование. Мне больше по душе вот эта ссылка.
Конечно, придраться, при таком объяснении, есть к чему, но если рассказывать человеку, никогда не слышавшему о glvalues/xvalues/prvalues/lvalues/rvalues «на пальцах», то именно эта статья, мне кажется, проще всего. На самом деле, я скинул эту статью, ради вот этой ремарки с картинкой:
В комментариях же, уже идёт обсуждение о неточности/некорректности концепции…
Technically these are two kinds of rvalue: expressions denoting truly abstract values are prvalues (pure), while expressions denoting short-lived objects are called xvalues (expiring) [1] §3.10.
В комментариях же, уже идёт обсуждение о неточности/некорректности концепции…
…лямбда-выражения не принесли в язык новой функциональности (в оригинале — expressive power).
На русский язык «expressive power» вполне можно перевести как «выразительные возможности».
В одном видео с какой-то конференции Мейерс говорил, что пришел к выводу о нецелесообразности введения своего термина «универсальные ссылки».
Не видел честно говоря, до этого он во всех лекциях упорно вводил этот термин. Концепция конечно неоднозначная, кажется единственная альтернатива — формальные правила reference collapsing (про которые я не упомянул)
Действительно, спасибо. Тем не менее все похоже свелось к переименованию universal references -> forwarding references. Если так, то и говорить не о чем, все равно C++ никто не читает как английский текст но воспринимает как поток токенов, смысл токена лишь приблизительно совпадает с английским значением.
НЛО прилетело и опубликовало эту надпись здесь
Оба примера прямо из книги, я старался не модифицировать код без надобности.
Насколько я понимаю, в первом примере идет иллюстрация концепции — захват переменной класса через создание локальной переменной, предложенный Вами пример больше бы демонстрировал синтакс C++14 и не демонстрировал бы эту концепцию так же ясно.
Над вторым утверждением я тоже долго думал, если правильно понял, то речь идет скорее о теоретическом подходе — анализируя весь код (и при условии что потоки тоже строго детерменистичны) можно вычислить где и когда будет вызван деструктор. В общем спорное утверждение, корректное только в определенном контексте, согласен.
Насколько я понимаю, в первом примере идет иллюстрация концепции — захват переменной класса через создание локальной переменной, предложенный Вами пример больше бы демонстрировал синтакс C++14 и не демонстрировал бы эту концепцию так же ясно.
Над вторым утверждением я тоже долго думал, если правильно понял, то речь идет скорее о теоретическом подходе — анализируя весь код (и при условии что потоки тоже строго детерменистичны) можно вычислить где и когда будет вызван деструктор. В общем спорное утверждение, корректное только в определенном контексте, согласен.
НЛО прилетело и опубликовало эту надпись здесь
Ну да, именно такой подход и предполагался — деструктор декларируется в обьявлении класса и определяется в .cpp файле. Это нормальный подход, который используется и в др. случаях. В книге обьяснено подробнее, но не мог же я все целиком переписать.
НЛО прилетело и опубликовало эту надпись здесь
А почему бы и нет? Зачем кстати пустой деструктор писать вообще?
Но если он написан то move-конструктор придется в любом случае описывать, компилятор уже умывает руки.
Но если он написан то move-конструктор придется в любом случае описывать, компилятор уже умывает руки.
НЛО прилетело и опубликовало эту надпись здесь
Ну если подходить к делу так, то да, конечно. Есть однако же и другая точка зрения — shared_ptr здесь не подходит (как минимум — избыточен), и зачем я буду его использовать если можно использовать unique_ptr всего лишь определив деструктор.
Я сам кстати вообще не любитель pimpl, если вы об этом.
Я сам кстати вообще не любитель pimpl, если вы об этом.
Если уж вам так принципиален дефолтный деструктор, можно сделать так:
// Header file
class C {
private:
class Pimpl;
struct Pimpl_deleter {
void operator() (Pimpl* pimpl_ptr);
};
std::unique_ptr<Pimpl, Pimpl_deleter> pimpl_ptr;
};
// Source file
class C::Pimpl { ... };
void C::Pimpl_deleter::operator() (C::Pimpl* pimpl_ptr)
{ delete pimpl_ptr; }
Но вообще ваше желание странновато.Здесь в статье или в оригинале ошибка:
Правильно
template<typename T> void setName(T&& _name) {
name=std::forward<std::string>(_name);
}
std::forward<std::string>(_name)
всегда вернет rvalue ссылку std::string&&
, независимо от типа ссылки _name. То есть, будет работать как std::move
.Правильно
std::forward<T>(_name)
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Аннотация к «Effective Modern C++» Скотта Майерса. Часть 2