totoro
> Так ведь об этом и речь, что воодушевившись новомодными стандартами можно аргументированно испохабить любой код
стандарт тут не причем, не соблюдая контракт ты будешь получать бо-бо еще раз повторюсь практически всегда.
> подкрепляя собственную правоту жаркими спорами и пылкими фразами, вроде
ладно, поскольку ты не понимаешь что такое контракт. Я тебе объясню на пальцах. Это соглашение которое принимает пользователь вызывая функцию в котором говорится что это функция ожидает и что она делает, что она гарантирует и что нет. Обычная документация является таким контрактом. Если ты напишешь:
std::string name = "Красная шапочка"; name[name.length()] = '!'; // нарушение контракта. Получай по башке. name.at( 150) = '!'; // ок, ты ленив держи исключение, состояние строки не изменится.
totoro
Потому что не надо ставить аппрувы если изменение не покрыто тестом
Хотя я понимаю что есть задачи где это непонятно в принципе что тестировать, а критерий правильности хрен пойми как формализовать
Это тот случай, когда старое доброе проверенное временем:
void set_name(std::string /* без выпендронов */ new_name) { this->name = std::move( new_name); } govno.set_name( "vasya" + to_string( vasya_index)); // эрвалью (мув) govno.set_name( other_govno.name); // эльвалью (копия)
А ещё лучше:
class govno { public: /* оопу - в опу */ std::string name; }; govno.name = "vasya" + to_string(vasya_index); // эрвалью (мув) govno.name = other_govno.name; // эльвалью (копия)
Имбирная Ведьмочка
> оопу - в опу
Мой любимый архитектурный паттерн, кстати (недавно кто-то спрашивал).
Имбирная Ведьмочка
> Это тот случай, когда старое доброе проверенное временем:
Разве это старое? (и вовсе недоброе). Старое доброе это когда:
void set_name(const char* /* без выпендронов */ new_name) { std::strncpy( this->name, new_name, this-length); }
Имбирная Ведьмочка
> Это тот случай, когда старое доброе проверенное временем:
И будет лишняя копия если забудешь сделать мув
totoro
> Разве это старое?
С конца 2011 года прошло больше 12 лет. Вполне уже старое.
totoro
> const char* /* без выпендронов */ new_name
1. Ломается на встроенных нулях.
2. Не отличает мув от копии.
totoro
> std::strncpy
3. Ломается на строках длиннее икс. Причём ломается молча — строка просто обрезается, при этом даже лишаясь нуль-терминатора, вместо того, чтобы явно кинуть эксепшон и сказать пользователю об ошибке.
1 frag / 2 deaths
> И будет лишняя копия если забудешь сделать мув
Ну и пофигу, на самом деле. В критичном по времени коде у тебя вообще не должно быть std::string ни в каком виде.
Пока изучаю defailt ctor
Есть в литературе якобы мнение (во всяком случае я так считал, поскольку ранее читал где-то), что конструктор по умолчанию генерируется (синтезируется), если он не предоставлен пользователем (пользователь как я понял, это разработчик класса). Сейчас читаю Липпмана "Inside C++ Model", где он развенчивает данный миф.
Я вроде понял, что такое trivial default ctor из стандарта в 12.1.4.
Ежели кратко:
1) не предоставлять его самому
2) не должно быть виртуальности ни у базовых классов, ни у членов-данных вообще никакой
3) нет ни одного инициализатора-фигурные-скобки-или-равно ни у одного члена во всей этой иерархии как классов, так и их членов-данных
То есть, тривиальным будет конструкторм по умолчанию для класса, если класс простой как болванка без разных "хитрожопых" штук и сделан из таких же простых членов данных и базовых классов.
Что-то вроде
class Vector { public: float x, y, z; };
Однако дальше не нашел, а к чему это вообще там сказано. Ну он объявляется неявно, это я понял. На практике он не генерируется (я проверял в Ida Pro, выключив оптимизацию) - нет никакого вызова. Есть ли в стандарте (или еще где-то) описание, когда генерируется его реализация?
Профессор ДиректИкс
А как ты переменную заводил?
Vector v{}?
1 frag / 2 deaths
> А как ты переменную заводил?
> Vector v{}?
просто Vector v; // надо попробовать твой вариант
как только я пробовал инициализировать член-данные внутри класса, либо объявлял виртуальную функцию, то dflt ctor синтезировался.
Кстати, если явно прописать Vector() = default, то то же ничего не синтезируется (пока не появится штука, ломающая тривиальность).
totoro
> Хочется чтобы там где надо по значению - передавалось по значению, а там где по ссылке- по ссылке.
Ну вот поэтому лучше std::string в том случае передать по значению, всё правильно.
> void set_name(const char* /* без выпендронов */ new_name) { std::strncpy(this->name, new_name, this-length); }
И как теперь govno.set_name("vasya" + to_string(vasya_index)); сделать?
Профессор ДиректИкс
Раньше был (уже ныне устаревший) термин plain old data. Смысл в том, что для plain old data не является UB выделение через malloc и сразу с ходу в карьер использование.
Понять что это достаточно легко по определению: чтобы компилятор ни шагу не обязан был совершить при создании переменной.
А этих шагов в общем случае много: инициализация указателя на виртуальные таблицы, конструирование сложных членов и т.п.
=A=L=X=
> Понять что это достаточно легко по определению: чтобы компилятор ни шагу не обязан был совершить при создании переменной.
Я понял интуитивно, об этом же пишет и Липпман в своем "Inside the C++ Object Model":
"The standard then goes on to iterate the conditions under which the implicit default constructor is considered
trivial. A nontrivial default constructor is one that in the ARM's terminology is needed by the implementation
and, if necessary, is synthesized by the compiler."
То есть, конструктор по умолчанию синтезируется, если того требует реализация ( а не логика программы). Но хорошо бы в стандарте это почитать, но продираться через его похожие на юридический текст формулировки довольно нелегко.
Тема в архиве.