Как стать автором
Обновить

Комментарии 63

Лучше покажите как строятся «хорошие программные модули» на современном C++
методичка к третьей лабораторной работе первокурсников по информатике?
Можно к правилам хорошего тона добавить еще пожелание использовать в проде фишки стандарта, которого еще нет с осторожностью, не однократно подумав. Они к моменту релиза стандарта, могут пропасть из него…
Стандарт ещё на прошлом заседании был одобрен и уже давно отправлен в ISO. Так что боятся нечего насчёт С++17. Сейчас вся работа ведётся над C++20.
Зачем нужен тип enum

Еще полезен при использовании в switch тем, что компилятор выдаст предупреждение, если не все состояния учтены.
Благодарю за добротную статью. Очень пригодится в полноценном видении языка!
Насколько «литеральная инициализация» вообще востребована?
Ведь более-менее серъёзные программы обычно разделяют данные и код, загружая всё текстовое из внешних ресурсов…

С помощью литеральной инициализации можно собрать несколько значений, возвращённых функцией, в структуру и избежать out-параметров. То есть цель не в том, чтобы хардкодить данные. Цель в лёгкой трансляции, композиции и декомпозиции данных. В плане декомпозиции очень помогает structural binding, про который я забыл в статье. Так выглядит декомпозиция структуры (хотя 5 переменных за раз конечно перебор):


// композиция структуры
const auto book = BookStats{
    u8"Незнайка на Луне",
    { u8"Николай Носов" },
    { u8"детская", u8"фантастика" },
    576,
    1965
};
// декомпозиция структуры
const auto [title, authors, tags, pagesCount, publishingYear] = book;

P.S. добавил в статью примеры structured binding

Лично я вообще не понял, что автор имеет в виду под термином "литеральная инициализация". Судя по всему, к строкам и тексту это вообще не имеет никакого отношения, и "литеральной инициализацией" автор обозвал "aggregate initialization". Если это так, то нужно поправить публикацию.

Согласен, переименовал под термин "агрегатная инициализация". Спасибо!

До сих пор не понимаю зачем две одинаковые конструкции class и struct, уже давно нужно было выпилить struct. Никакой «локаничности» его использования нет.
а обратная совместимость есть\
На древнем С++ можно запрограммировать Матрицу. Эти нововведения мешают кодерам, разве что полезное — инит переменных, всё.

Кому и чем мешают нововведения?

Сергей, допустим, я описал тип struct или class с полем int, и я поленился написать конструкторы, а также ин-плейс инициализацию для этого поля. Я создаю инстанс моего типа (неявно используя дефолтный конструктор без параметров). Что говорит стандарт разных годов выпуска про значение int-поля этого инстанса?

Стандарты всех версий считают, что неинициализированное нестатическое поле остаётся в неопределённом состоянии до первого присваивания. Чтение из такого поля — неопределённое поведение. Между тем, в некоторых компиляторах такие поля инициализируются нулями в отладочной версии и не инициализируются после включения оптимизаций. Кроме того, вероятность получить в виде мусора тот же 0 достаточно высока.


Итог: ошибка проявляет себя не всегда, её трудно распознать. Проблему надо предотвращать в корне инициализацией при объявлении.


  • если в проекте используется анализатор C++ Core Guidelines, он должен сообщать о такой ошибке
  • если в проекте используют компилятор Clang, имеет смысл включить undefined behavior sanitizer в отладочных сборках
А что должен вернуть такой код на разных редакциях C++
#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;
}

Поменяйте строчки


    A *a=new A();
    B *b=new B();

на


    A *a=new A;
    B *b=new B;

И почувствуйте разницу между no initialization и default initialization.

Почувствовал
Спасибо! Понятно, что «проблему надо предотвращать в корне», я хотел разобраться, что говорит текущий стандарт (и как давно он так говорит). Если проблема не предотвращена в корне, ситуация становится вот какой (внизу в комментариях некоторые аспекты обсуждаются) — если для нового инстанса класса с дефолтным конструктором (и без ин-плейс инициализации POD-полей) не указана явно "()-инициализация" (например, написано ' new S; ', а не ' new S(); '), инстанс не инициализирован, в нём мусор. Однако, если "()-инициализация" указана (как в упомянутом ' new S(); ' или при вызове std::make_unique), текущий стандарт всё-таки диктует инициализацию нулями. Эта последняя деталь упоминается в некоторых обсуждениях на StackOverflow, например, но я не нашёл чёткую отсылку к стандарту. Полез смотреть сам (https://github.com/cplusplus/draft/blob/master/papers/n4687.pdf), нашёл вроде бы соответствующий пункт на странице 226:
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 можно под любую кофеварку бинарник собрать, в тоже время современный C++ ограничивает только популярными платформами, C++11 под WinXP уже не работает. Т.к. все используют инструменты которые так или иначе собираются C++ то это очень похоже на целенаправленный механизм выдавливания старых платформ. Например PHP и cygwin уже не пускается под XP, хотя особых оснований для этого нет. Собственно это еще одна из причин по которой на новые версии C++ не привлекательны.
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.

По умолчанию оно генерит бинарники не совмистимые с winxp и даже линковщику нельзя указать версию winxp только через editbin. Поэтому всё что будет собираться без будет без поддержки winxp.
«Visual Studio 2017 умеет собирать под WinXP при использовании тулсета v141_xp» — его специально сделали таким кривым?
Ладно microsoft, но кроме visual studio есть другие компиляторы. Так вот они наотрез отказываются под winxp компилить и пускаться. К чему бы это? Такая зависимость сильная зависимость от AVX или все резко осознали что без conditional_variables жини нет?

Просто поддержка старой ОС для разработчиков этих "других компиляторов" в меньшем приоритете, чем другие задачи. Вполне разумно при ограниченных ресурсах.

mingw на пример, как собирал бинарники, которые работают на xp, так и собирает.
работают ли новые версии самого mingw под xp не знаю, т.к. использую кросс компиляцию. Но старые версии mingw работали на xp.
mingw еще пока работает, но в нём нет статического runtime только с msvcrt, а вот cygwin уже нет.
Незнаю как вам но я наблюдаю такую картину, чем более современный C++ тем больше код становится запутанней и содержит больше ненужного кода. И портирование обратно на C++98 показывает что новые возможности никак не сокращают объём кода, а наоборот его увеличивают, при этом используются для совершенно не значимых мест.
Код становится запутанным из-за таких программистов, а не из-за C++ или стандартов.
Некоторые просто использую все возможные новые фичи языка, где нужно и где не нужно.
НЛО прилетело и опубликовало эту надпись здесь
Какие такие подходы изменились. Под DOS, Win3.1 и unix С/С++98 компилятор работает без проблем например digitalmars. При желании можно запустить в виртуалке или в dosbox-е. Почему C++11 так не может. Что такого он использует при генерации кода что ему мешает работать на старых системах?
«Речь идет про систему 16-летней» ага через 3года и на windows7 перестанет собираться. Хочется привести ссылку на https://geektimes.ru/post/281470/
Причем тут система, железо то тот же самое и форматы файлов те же. Почему вдруг появилась привязка к системе? Что такого требует «hello world» что ему мешает пускаться на winxp. Тут только одно объяснение — кому-то это выгодно.
Более того новые редакции C++ вводят эти самые deprecated и не очевидное поведение очень шустро. То что раньше можно было использовать становится UB. Уже кто-то говорил «В случае UB gcc старается генерировать максимально не корректный код». А чудеса оптимизации, когда бесконечные циклы вдруг возвращают управление.
Старый код который собирался и работал либо уже не собирается, либо собирается, но не пускается на старых системах, либо работает не корректно и надо отлавливать новые баги.
НЛО прилетело и опубликовало эту надпись здесь
«Ради нескольких процентов аудитории» — C++ стала попсой и расчитанной на 98% аудитории?
«терять в производительности или безопасности для всех остальных пользователей — как-то глупо» и в каком месте выросла безопасность и увеличилась производительность?
НЛО прилетело и опубликовало эту надпись здесь
> замените слово «аудитории» на «платформ/операционных систем/сред исполнения», надеюсь, смысл фразы станет понятнее.
Да. То есть среда для ширпотреба, но для этого есть другие языки. более «безопасные» и «управляемые»

> читайте комментарии внимательнее
Внимательно прочитал EncodePinter/DecodePointer у которых жутко сложная реализация примерно такого вида ptr^=CONST очень сильно увеличивают безопасность и производительность и требуют «Minimum supported client: WindowsXP SP2»
И что это делает абсолютно не возможным запуск компилятора? Какое отношение это имеет к компилятору? Его задача строить AST деревия и из них собирать бинарники. А тут целеноправлено линкеру запрещено указывать SYSTEM и SUBSYSTEM ниже Vist-ы. Каким боком тут новые функции user32? Неужели компилятор использовал именно эти методы которые удалили? Или не может работать без свеже добавленных? Накой хрен спрашивается в винде директория WinSxS?

1)WinSxS не для системных библиотек. А для библиотек стороннего По(в том числе и от МS)
2)В части stl работа с потоками/локалями может быть завязка на функции из нового winsdk, поэтому там скорее всего тоже будет #ifdef на версию кодогенератора.


3)Кодогенерация для этих подсистем происходит по разному(см EncodePointer да там xor, но xor с кукой из PEB которой тупо нет в not 5.x) Поэтому и запрещают старый subsystem. Если же так нужно используйте тулчейн с суффиксом _xp в котором компоновщик(который может полагаться на новые фичи pe loader а из новых версий windows) и кодогенератор будут работать по-старому

Видимо я плохо пишу по русски. Дело не в кодогенерации и не в EncodePointer, а в целенаправленном выпиливании поддержки определённых платформ. Это динамические библиотеки что мешает при отсутствии функции не использовать её или использовать аналог. И потом зачем EncodePointer если есть EncodeRemotePointer сразу привязываемся к Win10.

Что печально именно такое поведение и будет в дальнейшем. Я не могу понять почему это всех устраивает. Вам ограничивают свободу действий и все хлопают в ладошки и просят добавки. При этом так ведут себя все не только 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

Просто не используя новые фичи C++ всё по прежнему работает на всех виндах. А есть новые, то только win7+ и в последствии только winx+. Стоят новые фичи этого? Если что-то общего пользования делать, то С++ как кандидат отпадает и остаётся только C но более консервативен. Например lua написана на C и его без проблем можно собрать под что угодно, хоть под дос и оно будет работать на каком-нибудь ICPDAS-е.
НЛО прилетело и опубликовало эту надпись здесь
Да издеваюсь. Все критически необходимые функции никто не менял. Подход называется не «вернём всё в зад», а «не стоит сжигать мосты».
Вы утверждаете, что новый С++ не подходит для множества платформ? Посмотрите, под что умеет компилировать GCC, и почитайте кучу историй о том, как применяют С++ на МК. Всё там нормально с этим.
Мы применяли какое-то время. Включение исключений увеличивало код на 20%. Стандартную библиотеку использовать тоже было практически невозможно, потому что она раздувала код очень сильно. И жрала RAM, которой было не очень то и много.

Поэтому, исключения мы всё-таки оставили, но STL не использовали, реализовывали нужные примитивы вручную.

Что бы вы понимали, мы в 512кб ROM и 128кб RAM упихали RTOS, сетевой стек, графическую подсистему, файловую систему, криптографию, поддержку Unicode, драйвера для всякой периферии и собственно бизнес-логику.

На самом деле у нас только бизнес-логика была написана на С++, что бы упростить жизнь прикладным программистам. Если бы мы всё писали на С++, то оно просто не поместилось бы в МК,
НЛО прилетело и опубликовало эту надпись здесь
Вот тут путаница C++ уже давно не C++ а C++11, C++14, C++17, C++20…
Под линуксом такого безобразия нет как под виндой и там 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, "");
}
Нет я имел ввиду что область видимости была иной. Примерно так:
struct Fn {
    int x,y; enum { B0=1,B1=2,B2=4 };
    operator int() { return x; }
};

//int z=Fn{ .x=B1|B2, .y=.x^B0 };
int z;{struct Fn1:Fn{Fn1(){
    x=B1|B2;
    y=x^B0;
}}fn1;z=fn1;}

Так не вернули же. Только собираются в С++20.

Спасибо за последний пример с std::variant. А то я все думал, как в С++ сделать красивые algebraic data types вроде Haskell'овского Maybe (Nothing | Just a).

Разве это не std::optional. Так же стоит указать, что optional, variant, any и string_view(string_ref) давно есть в boost, так что можно не ждать 17-го стандарта, а уже использовать.

Сам Maybe — это optional, конечно. Но есть иногда желание возвращать что-то более хитрое, самый банальный пример (из статьи) — результат | код ошибки с сообщением. Либо хранить состояние вместе с его параметрами, но не делая под это полноценный класс.

НЛО прилетело и опубликовало эту надпись здесь
Да… Не думал, что когда-то в struct нельзя было использовать с инициализацией полей… Сейчас вроде и битовые поля стали поддерживать инициализацию.
Вроде всё в с++ изучил более-менее, а после прочтения статьи, у меня не осталось сомнений, что я всё ещё новичок.
Твое лицо, когда заказчик просит развернуть на RHEL6 >.<
Как известно, 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);

Браво! C++ от случая к случаю. Статья ставит мозги на место. Квалификация автора и участников коллёквиума вне всяких похвал. Спасибо.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории