Comments 43
Непонятно, зачем вообще создатели языка придумали эти все "неопределённые поведения".
-6
Скрытие переменной
Просто скрытие переменной ещё полбеды. А вот когда встречаешь код типа:
int i=42;
...
{
int i=i+2;
вот тут начинается самое веселье. Ибо Visual C++ считает, что во втором случае берётся значение предыдущей i, и к ней прибавляется 2, а вот GCC берёт значение второй i (ещё не инициализированной, ага).
Терминатор
Особенно сильно доставляют abort()'ы в сторонних библиотеках (увы, бывает и такое). Отладка программ с ними (молча схлопывающихся при каких-то редко воспроизводимых условиях) доставляют массу ни с чем не сравнимых эмоций (которые, правда, с трудом можно назвать положительными). Хочется взять и крепко пожать шею замечательным людям, написавшим это.
открывает наборы перегрузки для не членов и доступ для членов) (open overload sets for non-members and member access for members)
Скорее, «расширяемый список перегруженных функций для не-членов и доступ к членам класса для функций-членов».
+8
кдпв и подпись к ней — 10/10.
+4
Судя по експлореру, Волдеморт компилится только начиная с 5го GCC. В 4,9 фейлится.
0
UFO just landed and posted this here
Не понял, что за синтаксис
думал я чего-то не знаю (что, конечно же, возможно), но нет, оно даже в 17 стандарте не компилируется. В статье ошибка?
struct {
(int _) : _(_) {}
operator int() { return _; }
int _;
};
думал я чего-то не знаю (что, конечно же, возможно), но нет, оно даже в 17 стандарте не компилируется. В статье ошибка?
+4
Rust решает проблему зомби крайне просто с точки зрения разработчика — код просто не скомпилируется при попытке использования перемещенной переменной.
+4
Новые стандарты С++ являются чистым злом. Само их существование создаёт нишу для того, что возможно будет названо «кросс-компитяторным С++ программированием». По аналогии с печально известным «кросс-браузерным». Писатели стандартов наивно полагают что покойники встанут из могил и отрефакторят свой код, если использовали в нем идентификаторы названные ныне ключевыми словами? Вот тема для Halloween. IMHO
-3
Флаги у компиляторов никто пока что не отменяет.
+1
Комитет переживает за кейворды больше всех нас, вместе взятых.
Стараются переиспользовать существующие, например,
Или брать старые, вышедшие из употребления кейворды от старого Си (тот же
А при введении новых спецификаторов не запрещать их появление к контексте идентификаторов, например
Стараются переиспользовать существующие, например,
void func() = delete;
Или брать старые, вышедшие из употребления кейворды от старого Си (тот же
auto
— зарезервировано с бородатых времён K&R).А при введении новых спецификаторов не запрещать их появление к контексте идентификаторов, например
int override = 1;
+5
Это правда — хотя неясно почему. Поиск-с-заменой — процедура известная десятки лет. COBOL со своими сотнями ключевых слов — живёт себе и не думает умирать.
-4
Идентификаторы я помянул для краткости, беда не столько в них сколько в этом:
-O3 -frtti:
Ради экономии на несколько команд ASMа. Снёс бы, но не снесёт, ибо в гробу уже.
-O3 -frtti:
struct machine {
struct success; struct failure; struct started; struct invalid;
struct state_t{
virtual bool interface_meth0(machine*) {return false;}
virtual void interface_meth1(machine*, const bool&) {}
virtual long interface_meth2(machine*, const char*) {return 0;}
struct proc {
virtual void on(machine::success*) =0;
virtual void on(machine::failure*) =0;
virtual void on(machine::started*) =0;
virtual void on(machine::invalid*) =0;
virtual ~proc() {}
};
virtual void on(machine::state_t::proc*) = 0;
virtual ~state_t() {}
} * stat ;
virtual bool interface_meth0() { return stat->interface_meth0(this); }
virtual void interface_meth1(const bool&o) { stat->interface_meth1(this,o); }
virtual long interface_meth2(const char*o) { return stat->interface_meth2(this,o); }
virtual ~machine() {}
static struct success:state_t { void on(state_t::proc*o){ o->on(this);} } success_state;
static struct failure:state_t { void on(state_t::proc*o){ o->on(this);} } failure_state;
static struct started:state_t { void on(state_t::proc*o){ o->on(this);} } started_state;
static struct invalid:state_t { void on(state_t::proc*o){ o->on(this);} } invalid_state;
machine() :stat (&invalid_state) { }
};
const char* get_state_name (machine* m) {
// В этой строке "восставший-труп-автора" для С++11 снесёт "static"
// а для древней GCC оставит "static" на месте
static struct: machine::state_t::proc {
const char* name;
void on(machine::success*) { name="SUCCESS"; }
void on(machine::failure*) { name="FAILURE"; }
void on(machine::started*) { name="STARTED"; }
void on(machine::invalid*) { name="INVALID"; }
} visitor; m->stat->on(& visitor ); // make-visitor-call-visitor
return visitor.name;
}
Ради экономии на несколько команд ASMа. Снёс бы, но не снесёт, ибо в гробу уже.
get_state_name(machine*):
sub rsp, 24 #31.41
mov rdi, QWORD PTR [8+rdi] #39.5
lea rsi, QWORD PTR [rsp] #39.19
mov QWORD PTR [rsi], offset flat: vtable for get_state_name(machine*)::{unnamed type#1}+16 #38.19
mov rax, QWORD PTR [rdi] #39.19
call QWORD PTR [24+rax] #39.19
mov rax, QWORD PTR [8+rsp] #40.12
add rsp, 24 #40.12
ret #40.12
0
Что-то я не понимаю. В чем беда этого кода в свете нового стандарта? И зачем тут static?
+4
Для реализации новых фич разработчики компиляторов утяжелили реализацию обращения к static переменным, в частности ради threadsafty. В чисто однозадачном древнем коде, разработчик вклинивал статик для предотвращения стекового создания/удаления инстанции визитора при входе процесса в тело функции с инстанцией визитора внутри. Но в современном компиляторе это перестало быть действием ускоряющим выполнение функции. С точностью до наоборот. Визитор стало дешевле и безопаснее пересоздавать, чем обращаться к его статической инстанции.
0
Но код же работать не перестал? А выносить из стека структуру состоящую из двух указателей (считая vtable) — это была глупость даже на старом компиляторе. Выиграли пару команд, проиграли многопоточность на пустом месте.
Кстати, сборка GCC с опцией --enable-threads=single
не решает ли проблему этого кода?
+1
Перестал, обрёл признаки UB. Нет, не решает. Я достаточно обрезал этот фрагмент кода, до copy-paste в godbolt.org. Открыть сайт, выбрать С++, вставить фрагмент, Ctrl-Enter — скомпилировать и смотреть результитрующий ASM. Выбрать icc, gcc, или другой компилятор. Вставить опции компиляции. Смотреть результат.
-3
Причём тут godbolt.org? Все имещющиеся там компиляторы уже, как вы утверждаете, «испорчены» новыми стандартрами. Давайте возьмём GCC 3.4 (GCC 3.3 ваш код компилировать отказался, а править его я не хочу — потом ведь скажете, что не так исправил)
Вот как выглядит в нём get_state_name:
Вот как выглядит в нём get_state_name:
get_state_name(machine*):
.LFB27:
cmpb $0, guard variable for get_state_name(machine*)::visitor(%rip)
pushq %rbx
.LCFI0:
movq %rdi, %rbx
je .L55
movq 8(%rbx), %rdi
movl get_state_name(machine*)::visitor, %esi
movq (%rdi), %rax
call *24(%rax)
popq %rbx
movq get_state_name(machine*)::visitor+8(%rip), %rax
ret
.p2align 4,,7
.L55:
movl $__tcf_0, %edi
movq vtable for get_state_name(machine*)::._0+16, get_state_name(machine*)::visitor(%rip)
movb $1, guard variable for get_state_name(machine*)::visitor(%rip)
call atexit
movq 8(%rbx), %rdi
movl get_state_name(machine*)::visitor, %esi
movq (%rdi), %rax
call *24(%rax)
popq %rbx
movq get_state_name(machine*)::visitor+8(%rip), %rax
ret
.LFE27:
.size get_state_name(machine*), .-get_state_name(machine*)
И что так уж принципиально изменили новые стандарты? -1
Спасибо за ваш труд khim (не должно было собираться ниже чем GCC 3.8). А в опциях -O3 -frtti юзано? В самом деле старых компиляторов на гадболте не стало, а каких-то примочек странных добавилось. Теперь сравните со static инстанцией визитора и без static. И пора в личку, хотя тему Хеллоуина некро-код не портит IMHO.
0
А в опциях -O3 -frtti юзано?А чего это изменит?
Теперь сравните со static инстанцией визитора и без static.Опять-таки: чего вы хотите в коде разных компиляторов увидеть? Замену
atexit
на __cxa_atexit
, а затем и на __cxa_guard_acquire
/__cxa_guard_release
? Или что?И пора в личку, хотя тему Хеллоуина некро-код не портит IMHO.Тут не некро-код, тут некростандарты. Так-то код современных компиляторов стал только короче, а вся пляска с гардами и прочим подробно описано ещё в почтенном ARMе (год выпуска смотрим, да?), так что совершенно непонятно — почему вы вдруг на новые стандарты оплчились…
0
Думаю потому, что я старый пень, который успешно программирует уже 30 лет. Зная от силы, половину С++. И всё новое меня или пугает или просто бесит.
0
Проблема в том, что то, на что вы жалуетесь — это не «что-то новое». Это вещь, которая была в C++ с самого начала. Сейчас даже ограничили. В том C++, который описывался в ARM как раз почти 30 лет назад можно было вообще инициализировать
И деструкторы должны были вызваться только если static реально создался. Отсюда — все эти
static
и выражениями, которые могли зависеть от параметров функции!И деструкторы должны были вызваться только если static реально создался. Отсюда — все эти
atexit
/__cxa_atexit
. Они, я думаю, и в каком-нибудь Turbo C++ 1.01 были (хотя тут я уже на 100% не уверен — не пользовался). А вот __cxa_guard_acquire
/__cxa_guard_release
— это новая вещь, но она мало что меняет по сути: проверка флага всё равно осуществляется без всяких локов, они берутся только тогда, когда переменная оказывается неинициализирована. 0
Паранойя, — мой верный товарищ, говорит что всё новое, это к рефакторингу. И пока я точно не пойму, что все новые плюшки С++ даются без ущерба для быстродействия, буду нервно пялиться в АСМ, и ворчать. А без локов, это значит, что все статики нужно создать в main-thread, до того как прочие треды начинать использовать?
0
А без локов, это значит, что все статики нужно создать в main-thread, до того как прочие треды начинать использовать?А без локов — это значит что если из двух потоков кто-то попытается вызвать функцию со статиком, то может произойти что угодно. Что, если честно, делает их ядрёной бомбой в многопоточной программе. Вот тут товарищи из Microsoft рассказывают сказки про то, что это, типа — классно и правильно.
Но guard, защищающий переменную от повторной инициализации в однопоточной программе — там, как я уже говорил, с самого начала. Во всяком случае в том виде, в каком C++ описан в ARM'е.
Не помню когда добавили блокировку — но это случилось задолго до C++11, только Microsoft очень долго не хотел этого делать (что глупо: получалось, что в многопоточной программе статики в функциях были фактически бесполезны). Кажется в GCC 4.2, но могу и наврать. Давно это было. GCC 4.4 точно уже блокировки вызывает.
P.S. Всли программа реально однопоточная — то вы можете просто локи из функций
__cxa_guard_acquire
/__cxa_guard_release
убрать. -1
Нет, программа далеко не однопоточная. эта помесь паттернов «state» и «visitor», из ядра высоко-нагруженного мультизадачного сервера. Вход через визитирование состояний — для «view 0..N» иерахии, а запросы через «state» interface — для «model», «dependency graph computation model». Короче всё что может вызвать «lag» в «event driven main loop», выходящий за рамки «jitter», ставится на очередь в «tread-pool». Здесь это состояние «started». Транзакции ожидают 1..M машин из тредов. После чего визитор из main-thread проверяет очередь транзакций, т.е. набор попавших в них машин на состояние «success». Соответственно проводит, отменяет или оставляет в очереди (ожидает). Времени у него опять же в рамках заданного jitter. В принципе «main-thread» — привелигирован, как маститый хирург в больнице — заходит только в полностью подготовленную операционную, колдует пять минут и валит в следующую. А пациента в морг в палату из палаты носит толпа медиков рангом пониже. Как-то так.
0
с c++ близко знаком не первый десяток лет, но, внезапно, обнаружил необъяснимую фичу:
При кратком расследовании выяснилось, что «function(parameter);» подходящая функция с именем 'function' ищется в первую очередь в пространстве имён параметра 'parameter'.
я не могу предположить зачем понадобилось такое поведение и откуда взялось.
В результате путаница: std:: тут нужен там лишний — инструменты анализа негодуют.
{
std::vector<int> v1;
/*std::*/begin(v1); // Я не объявлял using namespace ::std или using std::begin
char buf[5];
std::begin(v1); // std:: обязателен
}
При кратком расследовании выяснилось, что «function(parameter);» подходящая функция с именем 'function' ищется в первую очередь в пространстве имён параметра 'parameter'.
я не могу предположить зачем понадобилось такое поведение и откуда взялось.
В результате путаница: std:: тут нужен там лишний — инструменты анализа негодуют.
0
я не могу предположить зачем понадобилось такое поведение и откуда взялось.Серьёзно? Тот факт, что суперсекретная мега-фича используется в [почти] программе «Hello, world» — вас не смущает?
Вот обьясните — как в этом коде:
#include <complex>
#include <iostream>
int main() {
std::complex<double> x(1,1);
std::cout << "Number is: " << x << "\n";
}
Компилятор нашёл и вызвал функцию
std::operator<<(ostream&, const complex&)
? Она ведь в std
живёт!В результате путаница: std:: тут нужен там лишний — инструменты анализа негодуют.Проблема в том, что его не всегда можно в принципе задать — см. пример выше.
Собственно статья в Википедии всю историю про Koenig lookup рассказывает в деталях. Влючая тот факт, что Koenig не изобрёл его, только описал…
+2
спасибо за разъяснение. я, конечно, был в курсе про (std::operator<<(ostream&, const complex&) эффект и его использование. но не распостранял его действие на именованные функции. этот Argument-dependent name lookup выглядит как обход одной частной проблемы (operator<<). не в курсе, есть ли ещё практичные применения(учитывая что в статье есть раздел Критика)?
0
Про концерт Swappable почитайте: en.cppreference.com/w/cpp/concept/Swappable
Опять-таки: Wikipedia ТОЖЕ об этом говорит.
Опять-таки: Wikipedia ТОЖЕ об этом говорит.
-1
Функции с cv-seq (мерзкие типы) — это больная тема. Несколько лет назад в своей статье как раз затрагивал проблемы, связанные с интерпретацией таких типов компиляторами. :)
Ссылка к слову пришлась
0
Sign up to leave a comment.
Бестиарий С++. Справочник по загадочным персонажам