Pull to refresh

Comments 54

C++ Concurrency in Action книга замечательная, есть в русском переводе: Параллельное программирование на C++ в действии. Могу сказать одно, что обязательна к прочтению.
Про Concurrency: недавно делал сравнение реализацию потоков в винде. и был удивлен. По сигнатуре потоки и иже с ними в STL взяты из boost, однако по скорости они проигрывают существенно.
По сути все зависит от конкретной реалиции STL ведь.
Ух ты, я и не догадался, что есть std::enable_shared_from_this. Написал свой велосипед (довольно кривой). Теперь выпилю велосипед, переделаю на std :)
Чем больше читаю эту книгу, тем больше прихожу в ужас от C++: еще больше неоднозначности и перегруженности в словах, еще больше «тонких» моментов, которые надо помнить, чтобы не допустить ошибку. Просто поваренная книга по отстреливанию ноги.
Эм? Что вас смущеат, по-моему все поведение логично и очевидно, если знать принцип построения языка.
Я понимаю, что всё логично, при расширении стандартов надо не поломать обратную совместимость и придерживаться определённого стиля.
Простой пример: move-семантика. Если бы мы разрабатывали C++ с нуля, разве стали бы мы придумывать std::move, std::forward, и синтаксический ужас вроде int&& myvar? Я не эксперт в написании компиляторов, но, мне кажется, это можно было бы реализовать на автоматическом уровне.
И таких вещей много, просто полистайте книжку, она состоит из «вопреки нашим ожиданиям компилятор делае так», «этот особый случай надо иметь ввиду».
UFO landed and left these words here
всегда надо вызывать перегрузку от Foo&&, скажете вы


Я скажу, что надо вызывать doWithFoo(const Foo&) в обоих случаях, т.к. передается lvalue. Возможно, я не уловил тонкую связь между ссылками и теоремой Ферма.
UFO landed and left these words here
Перемещение без захламления синтаксиса
UFO landed and left these words here
Справедливости ради стоит заметить, что очевидное поведение на то и очевидное, что должно быть понятно без всяких принципов.
Вот вам очевидно, что когда на тело не действует сила, то оно движется равномерно или находится в покое. Потому что вам известен курс элементарной физики и вы решили много задач по теме. А вот Аристотелю — нет.

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

Это я к чему, к тому, что очевидность — это не абсолютное понятие, а относительное.
Вот вам очевидно, что когда на тело не действует сила, то оно движется равномерно или находится в покое. Потому что вам известен курс элементарной физики и вы решили много задач по теме. А вот Аристотелю — нет.

Я бы назвал это логичным, но ни как не очевидным. Например, так ли очевидно, что в искривленных пространствах на тело, на которое не действует никакая сила, будет оставаться в покое или двигаться равномерно? А если пространство не просто выпукло/вогнуто (если это можно так назвать), а скручено? Где-то даже читал, что наше пространство может быть таким.
Искривление пространства тождественно действию силы гравитации. Поэтому в таком пространстве оно будет идти не прямолинейно и равномерно, а по отображению прямой в этом искривленном пространстве.

Это также очевидно, но уже из нас троих (Аристотель, прости) только мне, т.к. я знаю принципы и идеи построения ОТО.
Ну вот мы опять пришли к тому, с чего начали. Может быть очевидно, если имеются специальные знания, знания каких-то принципов. Так можно что угодно считать очевидным.

Тут конечно появляется граница, что считать специальными знаниями, а что общими. Я думаю так — если до чего-то можно додуматься, просто наблюдая мир вокруг себя, не прилагая целенаправленных усилий (т.е. не строить специально большие гудронныеадронные коллайдеры и подобное) — то можно считать такие знания общими, а выводы, полученные на их основе — очевидными.
Просто наблюдая знания не получишь. Знания получается в ходе декомпозиции собранного материала и выявления принципов работы.
Меня, как программиста на C, смущает type&&.
Символов им в ASCII не хватает или чего? '$' не использован еще, зато на бедный амперсанд вешают уже пятую функцию: побитовый AND (if (attributes & EFI_FILE_ATTRIB_FIXED) {...}), логический AND (if (a && b) {...}), взятие адреса (UINT8* ptr = (UINT8*) &a;), ссылочный тип (void f(const QByteArray& data) {...}) и теперь вот еще один ссылочный тип. С нетерпением жду новых стандартов, уже интересно, куда еще амперсанд допишут.
Доллар же вроде можно как идентификатор использовать, по крайней мере такое в gcc компилируется:
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++ никто не читает как английский текст но воспринимает как поток токенов, смысл токена лишь приблизительно совпадает с английским значением.
UFO landed and left these words here
Оба примера прямо из книги, я старался не модифицировать код без надобности.
Насколько я понимаю, в первом примере идет иллюстрация концепции — захват переменной класса через создание локальной переменной, предложенный Вами пример больше бы демонстрировал синтакс C++14 и не демонстрировал бы эту концепцию так же ясно.
Над вторым утверждением я тоже долго думал, если правильно понял, то речь идет скорее о теоретическом подходе — анализируя весь код (и при условии что потоки тоже строго детерменистичны) можно вычислить где и когда будет вызван деструктор. В общем спорное утверждение, корректное только в определенном контексте, согласен.
UFO landed and left these words here
Мне кажется, вас куда-то не в ту степь понесло. Детерминистично просто значит, что объект будет уничтожен сразу же, как только умрет последний указатель, а не в неопределенный момент времени после этого, когда будет запущен сборщик мусора.
UFO landed and left these words here
UFO landed and left these words here
Ну да, именно такой подход и предполагался — деструктор декларируется в обьявлении класса и определяется в .cpp файле. Это нормальный подход, который используется и в др. случаях. В книге обьяснено подробнее, но не мог же я все целиком переписать.
UFO landed and left these words here
А почему бы и нет? Зачем кстати пустой деструктор писать вообще?
Но если он написан то move-конструктор придется в любом случае описывать, компилятор уже умывает руки.
UFO landed and left these words here
Ну если подходить к делу так, то да, конечно. Есть однако же и другая точка зрения — shared_ptr здесь не подходит (как минимум — избыточен), и зачем я буду его использовать если можно использовать unique_ptr всего лишь определив деструктор.
Я сам кстати вообще не любитель pimpl, если вы об этом.
UFO landed and left these words here
Никакая оптимизация не поможет shared_ptr догнать unique_ptr в принципе, кроме того, дело не эффективности. Это принципиальный вопрос дизайна — эти два типа подразумевают разные паттерны использования и заменять один на другой — как есть рыбу ложкой.
Если уж вам так принципиален дефолтный деструктор, можно сделать так:
// 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; }
Но вообще ваше желание странновато.
UFO landed and left these words here
Здесь в статье или в оригинале ошибка:

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)
Не обижайте Майерса, ошибка моя :) Увлекся копипастом и не заметил.
Поправлено
Sign up to leave a comment.

Articles