Комментарии 63
Зачем нужен тип enum
Еще полезен при использовании в switch тем, что компилятор выдаст предупреждение, если не все состояния учтены.
Ведь более-менее серъёзные программы обычно разделяют данные и код, загружая всё текстовое из внешних ресурсов…
С помощью литеральной инициализации можно собрать несколько значений, возвращённых функцией, в структуру и избежать out-параметров. То есть цель не в том, чтобы хардкодить данные. Цель в лёгкой трансляции, композиции и декомпозиции данных. В плане декомпозиции очень помогает structural binding, про который я забыл в статье. Так выглядит декомпозиция структуры (хотя 5 переменных за раз конечно перебор):
// композиция структуры
const auto book = BookStats{
u8"Незнайка на Луне",
{ u8"Николай Носов" },
{ u8"детская", u8"фантастика" },
576,
1965
};
// декомпозиция структуры
const auto [title, authors, tags, pagesCount, publishingYear] = book;
Лично я вообще не понял, что автор имеет в виду под термином "литеральная инициализация". Судя по всему, к строкам и тексту это вообще не имеет никакого отношения, и "литеральной инициализацией" автор обозвал "aggregate initialization". Если это так, то нужно поправить публикацию.
Стандарты всех версий считают, что неинициализированное нестатическое поле остаётся в неопределённом состоянии до первого присваивания. Чтение из такого поля — неопределённое поведение. Между тем, в некоторых компиляторах такие поля инициализируются нулями в отладочной версии и не инициализируются после включения оптимизаций. Кроме того, вероятность получить в виде мусора тот же 0 достаточно высока.
Итог: ошибка проявляет себя не всегда, её трудно распознать. Проблему надо предотвращать в корне инициализацией при объявлении.
- если в проекте используется анализатор C++ Core Guidelines, он должен сообщать о такой ошибке
- если в проекте используют компилятор Clang, имеет смысл включить undefined behavior sanitizer в отладочных сборках
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void operator delete(void* ptr) { free(ptr); }
void* operator new(size_t sz) {
void *dst=malloc(sz);
if (dst) memset(dst,0xFF,sz);
return dst;
}
struct A { int x; };
struct B : A { A a; int y; };
int main(int argc,char** argv) {
A *a=new A();
B *b=new B();
printf("a->x=%d\n",a->x);
printf("b->x=%d\n",b->x);
printf("b->y=%d\n",b->y);
printf("b->a.x=%d\n",b->a.x);
delete b;
delete a;
}
if T is a (possibly cv-qualified) class type without a user-provided or deleted default constructor, then the object is zero-initialized and the semantic constraints for default-initialization are checked, and if T has a non-trivial default constructor, the object is default-initialized
Зачем мне нужно это выяснять в деталях, когда и так ясно, что проще всегда POD-поля ин-плейс инициализировать от греха подальше? Спор с коллегой, который ссылается на эту тонкость стандарта!
Проверяльщик VS 2017 какие-то случаи ловит, какие-то нет, к сожалению. Надо бы мне на досуге освоить проверяльщик Clang, наверно.
C++11 под WinXP уже не работает
Visual Studio 2017 умеет собирать под WinXP при использовании тулсета v141_xp
. Она поддерживает почти полностью C++11/C++14 и частично C++17, как в ядре языка, так и в STL.
Есть нюанс: при объявлении любой статической переменной в теле функции Visual Studio генерирует код, использующий thread local переменные, и на WinXP это может вызывать креш внутри DLL, подключённой к "старому" exe, не имеющему поддержки thread local переменных. Проблема и решение описаны на stackoverflow.
«Visual Studio 2017 умеет собирать под WinXP при использовании тулсета v141_xp» — его специально сделали таким кривым?
Ладно microsoft, но кроме visual studio есть другие компиляторы. Так вот они наотрез отказываются под winxp компилить и пускаться. К чему бы это? Такая зависимость сильная зависимость от AVX или все резко осознали что без conditional_variables жини нет?
Просто поддержка старой ОС для разработчиков этих "других компиляторов" в меньшем приоритете, чем другие задачи. Вполне разумно при ограниченных ресурсах.
работают ли новые версии самого mingw под xp не знаю, т.к. использую кросс компиляцию. Но старые версии mingw работали на xp.
Незнаю как вам но я наблюдаю такую картину, чем более современный C++ тем больше код становится запутанней и содержит больше ненужного кода. И портирование обратно на C++98 показывает что новые возможности никак не сокращают объём кода, а наоборот его увеличивают, при этом используются для совершенно не значимых мест.
«Речь идет про систему 16-летней» ага через 3года и на windows7 перестанет собираться. Хочется привести ссылку на https://geektimes.ru/post/281470/
Причем тут система, железо то тот же самое и форматы файлов те же. Почему вдруг появилась привязка к системе? Что такого требует «hello world» что ему мешает пускаться на winxp. Тут только одно объяснение — кому-то это выгодно.
Более того новые редакции C++ вводят эти самые deprecated и не очевидное поведение очень шустро. То что раньше можно было использовать становится UB. Уже кто-то говорил «В случае UB gcc старается генерировать максимально не корректный код». А чудеса оптимизации, когда бесконечные циклы вдруг возвращают управление.
Старый код который собирался и работал либо уже не собирается, либо собирается, но не пускается на старых системах, либо работает не корректно и надо отлавливать новые баги.
«терять в производительности или безопасности для всех остальных пользователей — как-то глупо» и в каком месте выросла безопасность и увеличилась производительность?
Да. То есть среда для ширпотреба, но для этого есть другие языки. более «безопасные» и «управляемые»
> читайте комментарии внимательнее
Внимательно прочитал EncodePinter/DecodePointer у которых жутко сложная реализация примерно такого вида ptr^=CONST очень сильно увеличивают безопасность и производительность и требуют «Minimum supported client: WindowsXP SP2»
Если интересно — вот полный список изменений API:
https://abi-laboratory.pro/compatibility/Windows_5.0_to_Windows_6.0/x86_64/abi_compat_report.html
Вот конкретно по kernel32.dll:
https://abi-laboratory.pro/compatibility/Windows_5.0_to_Windows_6.0/x86_64/compat_reports/kernel32.dll/abi_compat_report.html
и т.д.
1)WinSxS не для системных библиотек. А для библиотек стороннего По(в том числе и от МS)
2)В части stl работа с потоками/локалями может быть завязка на функции из нового winsdk, поэтому там скорее всего тоже будет #ifdef на версию кодогенератора.
3)Кодогенерация для этих подсистем происходит по разному(см EncodePointer да там xor, но xor с кукой из PEB которой тупо нет в not 5.x) Поэтому и запрещают старый subsystem. Если же так нужно используйте тулчейн с суффиксом _xp в котором компоновщик(который может полагаться на новые фичи pe loader а из новых версий windows) и кодогенератор будут работать по-старому
Что печально именно такое поведение и будет в дальнейшем. Я не могу понять почему это всех устраивает. Вам ограничивают свободу действий и все хлопают в ладошки и просят добавки. При этом так ведут себя все не только m$ но и goolge не отстаёт.
Компилятор не должен быть завязан на платформу, на неё должны быть завязаны библиотеки, которые можно спокойно менять. А тут именно компилятор гвоздями прибивают. Именно C++ который участвует в сборке практически всё. И это остальное автоматом работает только на новой платформе. Любые попытки портировать обратно начинают требовать всё возрастающих затрат затрат. И логично что этим никто-заниматься не будет.
Вы не сможете собрать clang под winxp даже если захотите — гвоздей много, да и не особо то хочется. MinGW запрещено иметь свою статическую библиотеку, он линкуется строго с msvcrt.
Если собирать проект в VS2005 то результат пускается как на win10 так и на winnt4, если еще и манифест добавить то даже будет красиво.
Но всем подавай новые свистелки, в результате так тихо и незаметно всех ведут к светлому будущему WinX. В итоге новые версии каких-нибудь php, perl, python или putty будет пускаться только в WinX.
Ну вы же не жалуетесь что msvc aka cl.exe не может юзать другой stdlib кроме msvcrt? Не возмущается почему icc на sparc не работает и powerpc. Чем меньше платформ у компилятора с тем лучше он оптимизирует для них код. И если для поддержки старых платформ нужно тащить старые костыли, то уж лучше выделить эти костыли в отдельную версию кодогенератора, стоит сделала ms. С ним у вас как раз будут все фичи новых стандартов на winxp. Да Такова разработчик компилятора завязался на свою стандартную библиотеку. MinGw может линковаться статически libstdc++ и libgcc (-static-libstdc++ -static-libgcc).Вместо msvcrt можно использовать newlib, если что. P.S. EncodeRemotePointer нужен только для rpc
Поэтому, исключения мы всё-таки оставили, но STL не использовали, реализовывали нужные примитивы вручную.
Что бы вы понимали, мы в 512кб ROM и 128кб RAM упихали RTOS, сетевой стек, графическую подсистему, файловую систему, криптографию, поддержку Unicode, драйвера для всякой периферии и собственно бизнес-логику.
На самом деле у нас только бизнес-логика была написана на С++, что бы упростить жизнь прикладным программистам. Если бы мы всё писали на С++, то оно просто не поместилось бы в МК,
Под линуксом такого безобразия нет как под виндой и там gcc работает и кроскомпилирует. А в винде нет.
const auto book = BookStats{
.title = u8"Незнайка на Луне",
.authors = { u8"Николай Носов" },
.tags = { u8"детская", u8"фантастика" },
.publishingYear = 1965,
.pageCount = 576
};
Наконец, вернули то, что в Си было давным-давно!
struct Add {
double x=0, y=0;
operator double () { return x+y; }
};
double z=Add{ .y=20, .x=10 };
double z=Add{ .y=20, .x=10 }();
Перепутал оператор double с оператором (). Вот такое приведение кажется опасным. Ещё никогда операторы неявного приведения до добра не доводили.
Но выглядит очень круто, никогда не смотрел на именованные аргументы с этой стороны.
struct Fn {
int x; enum { B0=1, B1=2, B2=4, B3=8 };
operator int () { return x; }
};
int z=Fn{ .x=B2|B3 }
namespace bit_placeholders {
enum { B0=1, B1=2, B2=4, B3=8 };
}
struct Fn {
int x;
constexpr operator int() { return x; }
};
int main() {
using namespace bit_placeholders;
constexpr int z=Fn{ .x=B2|B3 };
static_assert(z == 12, "");
}
C++ has not had structs since 1985. It only has classes.
Спасибо за последний пример с std::variant
. А то я все думал, как в С++ сделать красивые algebraic data types вроде Haskell'овского Maybe (Nothing | Just a
).
Вроде всё в с++ изучил более-менее, а после прочтения статьи, у меня не осталось сомнений, что я всё ещё новичок.
Как известно, forward declaration перечислений появился только в С++11. Использую такую эвристику, чтобы преодолеть затруднения на старых компиляторах. Она так же решает вопросы перекрытия областей видимости элементов перечислений, но вручную, за счёт implicit конструктора.
struct WebUserLoginState
{
enum LoginState
{
none,
notLoggedIn,
loggedIn
};
LoginState loginState;
WebUserLoginState(const LoginState& loginState);
WebUserLoginState();
};
WebUserLoginState::WebUserLoginState(const LoginState& loginState)
: loginState(loginState)
{
}
WebUserLoginState::WebUserLoginState()
: loginState(none)
{
}
class User
{
public:
void setState(const WebUserLoginState& state);
};
User user;
user.setState(WebUserLoginState::notLoggedIn);
Типы struct, union и enum в Modern C++