• Правильное применение const?

    @romancelover
    программист C++ под Linux
    Параметры функции, объявленные как const ссылки, могут инициализироваться результатом выражения (rvalue), а обычные ссылки, без const - только присваиваемому выражению (lvalue). В С++11 появились также rvalue ссылки, которые могут быть изменяемыми. Сложные объекты лучше передавать в функции по ссылкам (или указателям), чтобы избегать копирования.
    Значит если параметр передаётся по ссылке, то ссылка с const может быть входным параметром функции, а без const - скорее всего выходным (или двунаправленным, который передаётся в функцию и там модифицируется, и служит результатом выполнения). Параметр по ссылке без const может быть входным, но это неудобно - в нём нельзя передать выражение, а только переменную (или в общем случае lvalue). И ещё это сбивает использующих функцию программистов с толку, наводя их на мысль, что параметр по ссылке должен модифицироваться внутри вызываемой функции.

    Сразу понятно назначение параметров, какой параметр входной, какой выходной. Параметр по значению всегда входной, по указателю - аналогично ссылке, с const только входной, без const скорее всего выходной (но не обязательно).
    rvalue ссылки нужны в том случае, если нужно сохранить объект, переданный в функцию, без копирования (объект по const ссылке пришлось бы скопировать внутри функции, а это неоптимально). Параметр по rvalue ссылке - тоже входной параметр функции, как и по const ссылке.
    Ответ написан
    1 комментарий
  • Инициализация элемента к нулю?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Процесс инициализации объекта является довольно сложным и местами запутанным.

    В контексте вопроса полезно будет рассмотреть два отдельных вида инициализации: инициализацию по умолчанию и инициализацию значением.

    Поведение инициализации по умолчанию говорит что для сложных типов будет выбран подходящий конструктор, а для тривиальных типов ничего сделано не будет. Эти правила говорят о том, что обязательная процедура инициализации объекта не всегда приводит к его фактической инициализации. Для тривиальных типов инициализация является фиктивной, а созданный объект остается неинициализированным.
    Тут можно обратить внимание и на то, что в поведении инициализации по умолчанию вообще ничего не говорится о вызове конструктора тривиального типа. Просто говорится о том, что при инициализации ничего не делается.
    Мы часто путаем вызов конструктора и инициализацию, т.к. смысл их близок, а сильные различия не всегда вспоминаются. Фактически конструктор тривиального типа вообще не фигурирует в процессе инициализации, но бывает проще запомнить что это именно он фиктивный и ничего не делает.

    Интереснее будет рассмотреть поведение инициализации значением.
    Форма T() может привести к вызову конструктора с пустым std::initializer_list, к вызову конструктора по умолчанию, а может свестись к инициализации по умолчанию в том случае, если конструктор по умолчанию удален или по своим причинам пропущен в пользовательском типе.
    Каждый, кроме последних двух, пункт поведения инициализации значением оперирует словом class, т.е. имеет отношение только к сложным типам. Последний пункт описывает поведение инициализации для всех неописанных выше случаев как инициализацию нулем.
    И именно инициализация нулем написана в примере вопроса.

    Форма T() хорошо подходит для инициализации локальных временных объектов, а для инициализации именованных объектов лучше подходит форма T object {};.
    Форма T object = T();, на самом деле, выполняет инициализацию копией, где в качестве копии используется локальный временный объект T().

    В результате, конкретно конструкция T() или T{} может привести к вызову конструктора по умолчанию или вызову конструктора от пустого std::initializer_list для сложного типа и инициализацию нулем для простого типа.
    Это - очень полезное поведение в метапрограммировании, где у тебя может быть шаблон типа, в котором требуется по значению разместить объект с типом параметра шаблона. Этот объект требуется инициализировать правильным образом, но ты никак не можешь классифицировать тип, передаваемый параметром шаблона. Благодаря инициализации значением можно не задаваться вопросами природы типа из шаблонного параметра.

    Но увлекаться этим подходом тоже не стоит. Я предпочту вариант std::string string; варианту std::string string = std::string(); потому что для std::string инициализация по умолчанию сделает точно то же самое, будет эффективнее (т.к. во втором случае будет копирование) и будет понятнее.
    Код HRESULT res = HRESULT(); стоит заменить на инициализацию одним из базовых значений. Например - кодом S_OK. Это создаст понимание базового состояния программы в месте определения переменной. Тип HRESULT имеет очень сильную семантику и сразу о многом говорит подготовленному читателю.
    Только HWND window = HWND(); или HWND window{}; на самом деле смотрится к месту, т.к. в WinAPI нет предопределенного начального значения и объект проще провести через стандартную инициализацию значением. Уж этот краевой случай разработчики WinAPI должны предусмотреть.
    Ответ написан
    1 комментарий
  • Инициализация элемента к нулю?

    maaGames
    @maaGames
    Погроммирую программы
    Вызывается конструктор по умолчанию. Для указателя это 0, он же nullptr.
    HWND и HRESULT это void*.
    Это корявая запись в стиле C#. В С++ лучше инициализирвоать сам создаваемый объект, чтобы избежать копирования (не в данном случае, а вообще, если объект имеет сложный тип). Т.е. писать сразу:
    HWND window(); или HWND window = nullptr;
    Ответ написан
    7 комментариев
  • Инициализация элемента к нулю?

    @Mercury13
    Программист на «си с крестами» и не только
    1, 3. Просто совпадение, обычно потому, что компиляторы в отладочном режиме всё инициализируют нулями (проще отлаживать). На это нельзя рассчитывать, пиши HWND window = nullptr.
    2. Лучше написать std::string str;, да и всё. Можно рассчитывать, что будет пустая строка.

    Почему 1,3 плохо, а 2 хорошо? Потому что HWND и HRESULT — синонимы для встроенных типов (не то указатель, не то число того же размера), а у них конструктор по умолчанию ничего не делает. А string — нормальный себе объект.
    Ответ написан
    4 комментария
  • Лучшие источники для изучения CPP?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    В самую первую очередь - это будет документация языка. Ее очень удобно использовать как справочник. Это - твой самый первый источник информации по любому вопросу.
    isocpp поддерживается создателем языка и содержит море полезной информации.
    C++ Core Guidelines является манифестом пользователя C++. Его знать обязательно. Документ регулярно дополняется.

    More C++ Idioms. Шаблоны проектирования имеют свою собственную многомерную классификацию. Идиомы - это функциональные шаблоны проектирования, применимые, как правило, или для конкретного языка, или для некоторого семейства языков. Эта открытая книга помогает ориентироваться в некотором начальном наборе идиом конкретно для языка C++.
    C++ Patterns - еще один полезный ресурс для изучения применимых к C++ шаблонов проектирования.
    С Fluent C++ ты уже знаком.
    Безусловно, блог создателей PVS-Studio.
    Habr, конечно же.
    Блогов очень много, их можно просто найти по релевантной фразе "C++ blog".

    Помимо этого есть большое количество каналов от разных конференций, доклады на которых всегда помогают понять язык лучше.
    С++Russia,
    C++Now,
    Pacific C++,
    CppCon,
    code::dive,
    Meeting C++.

    Так же будет полезно изучить книги авторов:
    Андрея Александреску,
    Герба Саттера,
    Девида Вандервуда,
    Скотта Мейерса,
    Роберта Мартина.
    Есть и другие очень полезные авторы. Тут у меня, пожалуй, только самый основной список.

    Последим, и самым важным, источником будет текущая рабочая версия стандарта языка, а так же пара лабораторий для практики: Compiler Explorer и C++ Insights.
    Ответ написан
    Комментировать
  • Предупреждение при использовании последнего элемента массива?

    Rsa97
    @Rsa97
    Для правильного вопроса надо знать половину ответа
    почему при указывании 3 элемента, он начинает считать с 1
    Кто вам такое сказал? Элементы массива в C++ всегда нумеруются начиная с нуля.
    Предупреждение для того и даётся, чтобы вы исправили код. Иначе во время работы программы могут быть самые неожиданные эффекты, начиная от некорректных вычислений и заканчивая вылетом программы из-за исключения при попытке доступа к чужой памяти.
    Ответ написан
    Комментировать
  • Работа с образованием 9 классов в IT, возможна ли?

    @galaxy
    Конечно, возможна.
    Но поначалу будут проблемы, т.к. куча резюме идет в мусорное ведро только по одному критерию наличия вышки, а если и пригласят, замучаетесь каждому HRу доказывать, что вы на самом деле умный и талантливый, просто разочаровались в системе образования. Так что советую заранее заготовить слезоточительную историю.
    С другой стороны, нормальные люди все же смотрят на реальные знания и опыт, способный человек свое место всегда найдет. Поговонокодите немного за еду, поднабретесь опыта, там, глядишь, и заочно какую-нибудь вышку закончите. Не надо заранее хоронить надежды.
    Ответ написан
    Комментировать
  • Работа с образованием 9 классов в IT, возможна ли?

    inoise
    @inoise Куратор тега Карьера в IT
    Solution Architect, AWS Certified, Serverless
    В 100500 раз. Я как и многие пришёл в it не имея вышки (сам бросил). Практически никогда не спрашивали, да и обычно это было просто как довесок для сравнения схожих по уровню кандидатов. Куда важнее военный.

    Учитывая что в it вышка даёт базис только в небольшом числе направлений - можно даже не беспокоиться на этот счёт. Даже для релокации уже blue card сдался и отменил требование диплома для it специалистов
    Ответ написан
    7 комментариев
  • Возможно ли сократить данный цикл?

    gbg
    @gbg Куратор тега C++
    Любые ответы на любые вопросы
    using namespace std;
    for(const auto& i:vec_s)
    {
        cout << i << '\n'; //выводим \n чтобы не сбрасывать буфер терминала на каждой строке
    }
    cout << flush;
    Ответ написан
    Комментировать
  • Vector - объявление элементов, в чем разница между двумя способами?

    @Mercury13
    Программист на «си с крестами» и не только
    Первый способ: создаётся initializer_list, вызывается конструктор. Наиболее эффективен для простых тупых объектов.

    Второй способ эквивалентен push_back(string("vec1")) и сильно полагается на оптимизацию передаваемых параметров. Наименее эффективен.

    Третий способ гарантированно не создаёт промежуточные объекты (вызывает конструктор string(const char*) уже на месте). Вместе с reserve(3) наиболее эффективен для управляемых объектов.

    UPD. 2 и 3 для string() не очень сильно различаются из-за эффективного переноса. А вот для более крупных объектов 3 лучше.

    UPD2. Начиная с Си++20, 1 лучше, но только из-за того, что Си++ научился делать полноценные string’и при компиляции — то есть управляемый объект string обзавёлся некоторыми чертами тупого.

    UPD3. Ни один компилятор пока constexpr string пока не поддерживает. Но ведь ни у кого стабильного 20 пока нет, правильно?
    Ответ написан
    9 комментариев