• Почему Doom портируют куда только можно?

    mayton2019
    @mayton2019
    Bigdata Engineer
    Doom - это была одна из первых игр где была реализована в полной мере техника BSP (Binary-Space-Partition). Это позволяло в реальном времени очень быстро сортировать и рендерить полигоны без привлечения Z-Buffer. (Рендеринг был чисто прогарммный. На то время еще не было ускорителей 3Д графики и видеокарточка умела только отображать память на экран. ) Игра не была по настоящему трехмерной. А только рализовывала 1 этаж. Рендерить надо было только пол и потолок. Для каждой точки где стоял персонаж. И наклонных горизонтальных поверхностей в ней не было как раз по этой причине. Ее называли 2.5-мерной игрой. Было также много идей оптимизации вычислений. Например вместо вещественных чисел - целые 32х битные с fixed point.

    В скобках замечу что это был 1993 год и на дворе было царство Intel 286/386. Это были машины с тактовой частотой меньше ваших телефонов (Pentium только только релизнулся и еще ни у кого не был). И трехмерный Doom был прорывом. На моем тогда еще 386SX от подтормаживал но играть было можно. И со звуком для SoundBlaster. И конечно это было лучше чем Wolfenstein.

    Аналогичное было реализовано в Duke Nukem 3d. Но Дюк был менее популярен ИМХО. Были и масса других 3д игр в жанке RPG но они были не такие динамичные. Походовые в основном.

    Вобщем сегодня если вы гейм-дев то вам желательно хотя-бы ознакомиться с техниками оптимизации которые были в Doom. Можно почитать и про Quake но это просто развитие идеи BSP деревьев для полного 3D с 5 или 6 степенями свободы. Еще можно почитать про реализацию игры Esctatica. Там не на полигонах а на шариках или эллипсах реализована игровая графика. Тоже интересно.

    UPD: Заменил MMORPG на RPG.
    Ответ написан
    6 комментариев
  • Какие инструменты нужны для разработки игр?

    @MarkusD
    все время мелю чепуху :)
    Сегодня существует ровно два базовых направления разработки конкретного коммерческого проекта.
    Способ первый: купить лицензию или подписку на уже готовый инструмент разработки и заняться непосредственно разработкой своей игры.
    Способ второй: иметь в своем штате команду разработчиков собственного инструмента, на базе которого можно заняться разработкой своей игры.

    Первый способ популяризирует публичные универсальные инструменты разработки.
    Второй способ эксплуатирует проприетарные инструменты.

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

    DirectX, Vulkan и OpenGL, равно как Metal и ряд проприетарных GAPI некоторых закрытых платформ, не являются графическими библиотеками. Это все - Graphics Application Programming Interface - GAPI.
    Это - низкоуровневые интерфейсы драйвера GPU, позволяющие эксплуатировать ресурсы видеокарты в своих целях. Не только для рисования чего-то, а для ИИ, ML, сложных статистических вычислений, предсказаний и прочих расчетов на больших объемах данных.
    Под капотом любого инструмента, будь-то проприетарный или публичный, в его графическом слое используется один или несколько GAPI. Без этого никак.
    OpenGL, как и DirectX 11, нисколько не устарели, поскольку предоставляют упрощенный интерфейс управления ресурсами GPU. Они используются тогда, когда разработчикам не нужны самые тонкие механизмы управления ресурсами GPU, которые предоставляют DirectX 12 или Vulkan. Потому что последние, помимо прочего, требуют от разработчиков более глубокой экспертизы и больше ресурсов на разработку всего того же, что на OpenGL и DirectX 11 реализуется меньшими силами и за меньшее время.

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

    Информации по каждой отдельной области разработки игр хоть отбавляй. Ее настолько много, что одному человеку за жизнь не усвоить. Поэтому от современного специалиста сегодня требуется спрофилироваться, т.е. определиться со своим профилем работы и стать экспертом.
    Я больше 15 лет занимаюсь разработкой игровых движков и медиаферймворков. Более 10 лет занимаюсь коммерческой разработкой кросслпатформенных инструментов. Я начинал свое обучение по книгам и документации для всех интересующих меня областей еще 20 лет назад. Я самостоятельно освоил множество API, включая графические, сетевые, звуковые и API целевых платформ, используя книги и документацию. Экспертные знания C++ и прочих языков я получил тоже через изучение документации, стандартов и книг.
    Я могу сказать что обучаться по книгам и документации можно и самостоятельно. Еще можно заплатить деньги и получить более точечные знания через их интерпретацию на распространенных сегодня онлайн-курсах. Такие знания не всегда бывают лучше полученных самостоятельно, но времени на освоение того же объема знаний на курсах уйдет меньше чем при самостоятельном изучении. Иными словами, занятия на онлайн-курсах не отменяют важности самостоятельного изучения основных источников информации.
    По открытым видеоурокам на ютубе и прочих видеохостингах обучаться нечему. Цель этих видео - чтобы зритель посмотрел рекламу и этим принес доход автору.
    Ответ написан
    2 комментария
  • Как имея позицию и направление двух объектов в пространстве координат y, x, z, повернуть один объект в другой?

    @MarkusD
    все время мелю чепуху :)
    Решением вопроса будет скалярное произведение двух векторов. Операция еще называется Dot Product.

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

    В качестве бонуса.
    Чтобы определить направление вращения, можно воспользоваться векторным произведением, еще называемым Cross Product.

    Векторное произведение трехмерных векторов позволяет определить нормаль к плоскости, образованной этими векторами. Нормаль будет направлена в одном из двух направлений от плоскости, в зависимости от расположения нуль-векторов относительно друг друга. Если в качестве левого аргумента взять все тот же нуль-вектор локального объекта, а в качестве правого - тот же нуль-вектор целевого, результатом произведения будет ось, вдоль которой и нужно выполнять доворот матрицы вращения.
    Ответ написан
    2 комментария
  • Можно ли без высшего образования работать в Геймдеве?

    @MarkusD
    все время мелю чепуху :)
    Без вышки работать можно не только в геймдеве. Вообще везде можно работать. Это иногда даже негласно приветствуется.
    За такую работу можно даже получать некоторые деньги, которых будет хватать на жизнь.

    Я работал с несколькими самородками. Эти ребята - реальные спецы своего дела. Вышка им бы только мешала в их работе, оттягивая на себя их реально ценное время. Они многого добились и продолжают добиваться. Это такие немного особые люди. А для всех остальных людей без образования будут мои следующие строки.

    Никакой более-менее серьезной должности, зарплаты или даже роли без высшего образования ждать не стоит. Не продвинут, не зачтут, не посчитают нужным. Потому что всегда есть более квалифицированный и подготовленный человек для этого. И лишь только ради открытия этих перспектив идти учиться в ВУЗ тоже не стоит потому что учебу в ВУЗе понимать нужно правильно.

    Оттарабанить 4-6 лет, вытягивая лямку нормативов на экзаменах без четкого понимания требований к тебе - это пустить деньги и время на ветер.
    В ВУЗ нужно идти за обучением самоконтролю, за обучением самодисциплине, за обучением самоорганизации. Вот те самые навыки, которые сегодня дает ВУЗ. Диплом магистра, бакалавра или специалиста - это дополнительный бонус. Разовьешь эти навыки самостоятельно - станешь одним из точно таких же самородков.
    В ВУЗ стоит идти за трамплином к знаниям. Чаще всего человека надо только подтолкнуть чтобы он стал специалистом. А толчком таким и является программа базового обучения в ВУЗе. Обучение базовое потому что его для последующей работы все равно хватать не будет. Дальше с этого трамплина нужно рвать во весь опор, находя и усваивая самые важные и самые нужные для своей работы знания. Осилишь найти все эти знания сам - ну чтож, ты один из немногих способных.
    По окончании ВУЗа человек не выпускается готовым к работе. На этом этапе он обладает только самыми базовыми навыками и дальше нужно продолжать учиться по профилю работы. Для этого есть стажировки, квалификационные курсы, а так же разнообразные книги и циклы статей для самостоятельного обучения.
    ВУЗы не готовят людей к работе, ВУЗы готовят людей к самостоятельной профессиональной подготовке.

    Люди без вышки спокойно работают в геймдеве рядовым персоналом десятки лет без шанса и желания повышения. Деньги идут, работа делается, проблем нет. Кое-где я таких людей наблюдал целыми отделами. Прекрасно работают, их все устраивает. Некоторым людям не нужны перспективы карьеры или роста, это нормально. А значит, таким людям и вышка тоже не сильно нужна.
    Ответ написан
    1 комментарий
  • Какая разница на практике между clang и gcc?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Однажды я на подобный вопрос уже отвечал, правда там вопрос был о кроссплатформенности, а не о трансляторах.

    Какая разница между clang и GCC.
    Разница большая. GCC обладает обширной поддержкой наследия идиом и конструкций из языка C, которые, вынужденно или по своей воле, поддерживает в современном C++.
    VLA, тип по умолчанию, всевозможные изыски синтаксиса C. Это все GCC не глядя принимает за C++ код и позволяет трансляцию.
    GCC даже сегодня многократно нарушает стандарты C++ просто потому что выбрал стратегию поддержки экзотической функциональности C в коде C++. Так же GCC не хвастается и скоростью поддержки стандартов C++.
    В 2016 году Google полностью отказались от поддержки GCC в Android NDK из-за слишком плохой поддержки стандартов и слишком свободного следования стандартам C++. В этот момент GCC стал неконкурентоспособным относительно оставшихся двух самых широко используемых трансляторов.
    Clang же, наоборот, сегодня считается, буквально, бастионом идеального следования стандартам C++. Clang точно поддерживает стандарты во всех деталях, максимально быстро интегрирует изменения и добавления стандартов, позволяет в самых первых рядах поиграться с функциональностью из драфтов следующего стандарта C++.
    Clang обладает обширной системой статической и динамической проверки кода: богатый статический анализ, возможность подключения санитайзеров, поддержка C++ Core Guidelines, очень качественные отчеты об ошибках трансляции, хорошая скорость трансляции.
    Это все ставит clang в предпочтение перед GCC на третьих для GCC платформах.

    О полной совместимости между трансляторами.
    Полная совместимость между трансляторами есть. Иначе я бы не мог делать то, что я делаю. А дело мое заключается в создании полностью кроссплатформенного кода, который однозначно собирается на всех целевых платформах и на всех них выполняется так же однозначно.
    Полная совместимость между трансляторами заключается в строгом соответствии кода выбранному стандарту C++. Всё, точка. На этом к трансляторам требования заканчиваются.
    Только тут есть небольшая проблема. Каждый транслятор по-своему поддерживает стандарт и по-своему реализует неоговоренные стандартом механики. Каждый транслятор имеет свои ошибки трансляции. И вскрывается это все именно в процессе работы над кроссплатформенным кодом.

    Я в своей работе видел многое. Я видел как при смене GCC на clang люди хватались за голову и отказывались от последнего просто потому что он нашел горы нарушений стандарта, которые молча принимал GCC. Я видел как группа из 5 человек 3 месяца рефакторила код при переходе с MSVS2015 на MSVS2017 (т.е. просто при смене версии транслятора) просто потому что разработчики из рук вон плохо знают используемый ими стандарт C++.
    Я видел ошибки в clang, приводящие к неверной генерации кода. Я видел ошибки в GCC, не позволяющие использовать его для кроссплатформенной сборки. Я видел ошибки в MSCL, в результате которых последний явно нарушает стандарт, а команда его разработки отказывается это исправлять потому что "иди нафиг".

    И, тем не менее, конкретно у меня есть возможность писать код ровно один раз и собирать его на 5 совершенно разных целевых платформ совершенно разными трансляторами, на которых этот код работает абсолютно равнозначно. Просто потому что я знаю стандарт и то, как этот стандарт поддерживают выбранные мной трансляторы.
    Ответ написан
    3 комментария
  • Почему lambda не захватывает this?

    gbg
    @gbg Куратор тега C++
    Любые ответы на любые вопросы
    Банальная порча всех итераторов, ссылок и указателей в тот момент, когда вы вызываете emplace у вашей свалки потоков второй раз. Лечение - избавиться от переаллокации при вызове emplace, путем вызова reserve(), или использовать контейнер, который не передвигает свое содержимое туда-сюда, например, std::list
    Ответ написан
    Комментировать
  • Почему бекенд не пишут на С++?

    RiseOfDeath
    @RiseOfDeath
    Диванный эксперт.
    Разработка сильно быстрее - как следствие она дешевле и Time to market сильно ниже, что в реально жизни гораздо важнее производительности (не всегда, но в общем случае это так).
    Арендовать еще один сервер очень часто может оказаться выгоднее, чем оплачивать лишних полгода разработки и выкатить продукт позже конкурентов.

    Пока вы сделаете и отладите свое классное быстрое приложение, все уже давно будут использовать кривую медленную Васину поделку.
    Ответ написан
    1 комментарий
  • Как в одном месте объединить разные блоки кода?

    @K1ingleonide
    #include "stdafx.h"
    #include <iostream>
    #include <cstdlib> // для system
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	setlocale(LC_ALL, "Russian");
    	cout << "Форматирование жёсткого диска HDD запущено" << endl;
    	for (int i = 60; i > 0; i--)
    	{
    		cout << i << endl;
    		Sleep(1000);
    	}
    	// system("pause"); // Только для тех, у кого MS Visual Studio
    	return 0;
    }


    Перед возвратом нужно было вставить нужный код, если нужно чтобы после завершения программы консоль не закрылась под виндой - то расскоментировать system("pause")

    1. Точка входа - _tmain
    2. setlocale задает локаль программы
    3. Вывод сообщения "Форматирование жёсткого диска HDD запущено" в консоль
    4. Вход в цикл
    4. 1 Вывод в консоль переменной i
    4. 2 Остановка потока на 1000 мс
    Ответ написан
    Комментировать
  • Как сложить - 1/3 и 1/5 на с?

    Ты делишь целые числа.
    Попробуй так
    #include <iostream>
    
    int main()
    {
        double a, b, ab;
        a = 1.00 / 3.00;
        b = 1.00 / 5.00;
        ab = a + b;
    
        printf("%f", ab); // 0.53
    }

    И вместо long double - просто double, тк %f - это для double
    Ответ написан
    Комментировать
  • Я придумал новый гениальный способ оптимизации игр?

    Zoominger
    @Zoominger
    System Integrator
    Ура, вы изобрели динамическую подгрузку локаций.
    Предлагаю ещё обратить внимание на круглый такой объект, на который можно навесить ось и что-нибудь на ней возить. Не знаю, как называется, не изобрели ещё.
    Ответ написан
    Комментировать
  • Как объяснить данный фрагмент кода?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    В данном коде демонстрируется типичная ошибка подмены типа (type punning [?]), ведущая к неминуемому UB [?] по стандарту.

    num в данном случае представляет собой шестнадцатеричное число, при выводе оно переводится в десятичное число типа double.


    Вот что стандарт говорит о подмене типа.
    [basic.lval#11]
    If a program attempts to access the stored value of an object through a glvalue whose type is not similar to one of the following types the behavior is undefined:
    (11.1) -- the dynamic type of the object,
    (11.2) -- a type that is the signed or unsigned type corresponding to the dynamic type of the object, or
    (11.3) -- a char, unsigned char, or std​::​byte type.

    Относительно unsigned long long тип double не обладает ни одним из требуемых стандартом свойств. Отсюда прямой вывод что данный код вносит UB.
    Поэтому под большим вопросом остается то, что выводится данным кодом.

    В итоге, правильным примером подмены типа на другой может быть только пример, где используется подмена на разрешенный тип представления объекта: char, unsigned char или std::byte для C++17.
    C++ Core Guidelines в секции C.183 однозначно не советует для подмены использовать union. Этого действительно не стоит делать даже при условии того, что все современные трансляторы понимают такие конструкции так, как подозревает писатель.

    Почему неверно работает, к примеру, такой фрагмент кода:

    Второй блок кода означает приведение значения из типа unsigned long long в тип double. В этом случае производится именно стандартное преобразование типа, а не подмена с целью интерпретации представления памяти num иным образом.
    0xC068C0C000000000 для типа unsigned long long означает 13864543383726325760, при преобразовании этого значения в тип double значение будет преобразовано в 1.3864543383726326e+19. В таком виде оно и будет выведено.

    Относительно первого блока кода, лучшим примером подмены типа для кода из вопроса будет такой:
    unsigned long long num = 0xC068C0C000000000;
    double representation = {};
    memcpy( &representation, &num, sizeof( num ) );
    cout << representation;

    И не стоит бояться обращения к memcpy тут. Данный код полностью соответствует стандарту и ожидаемо оптимизируется транслятором до желаемого результата.

    Мне не понятно, как работает такая конструкция, т.е зачем передавать именно ссылку на переменную num?

    Иногда требуется произвести приведение типа значения [?], иногда требуется произвести интерпретацию памяти этого значения другим типом. Это и называется подменой типа или алиасингом. Более детальную информацию о подмене типа, и алиасинге вообще, можно узнать из этой заметки.
    Ответ написан
    Комментировать
  • Как найти принадлежность точки к такому графику?

    Alexandroppolus
    @Alexandroppolus
    кодир
    "График" в твоей терминологии - серая область? Если да, то здесь просто набор ифов
    if (y > 0) {
      return (y >= x) && (y*y + x*x <= 9);
    } else {
      return (y >= -3) && (x <= 3) && (x >= -3) && !(y < -1 && y > -2 && x < -1 && x > -2);
    }
    Ответ написан
    2 комментария
  • Ошибка сегментирования (стек памяти сброшен на диск) c++. Как исправить?

    wataru
    @wataru Куратор тега C++
    Разработчик на С++, экс-олимпиадник.
    Ваша программа бесконечно уходит в рекурсию, у нее кончаетсся память и она падает. func(1, v) в цикле при i=0 вызовет func(1,v1), которая опять же вызовет func(1,v1), которая опять же... И так пока программе не поплохеет.
    Ответ написан
    Комментировать
  • Как связать игровое клеточное поле в Entity Component System?

    @MarkusD
    все время мелю чепуху :)
    Вообще, пространство игрового поля не очень хорошо выражать в терминах ECS. Игровое поле - это, как правило, монолитный участок логики, для которого лучше подошел бы подход CBSE(COP). Обычно игровое поле представляется композицией нескольких пространств, в которых работают механики игры. Такие пространства удобнее выражать компонентами из CBSE, а не компонентами из ECS.

    Однако, не смотря на это все, есть хороший способ реализации игрового поля терминами ECS. Особенно если игровое поле дискретно.
    Чтобы начать думать в этом направлении, сперва требуется изучить клеточные автоматы: [1], [2].
    Когда с принципами работы клеточных автоматов станет понятнее, стоит обратить свое внимание на шаблон инверсии контроля. При прямом контроле всем твоим миром руководит квант времени, который спускается с главного цикла до каждой сущности. Такой подход неприемлем на игровом поле в терминах ECS.

    Клеточный автомат оперирует возбужденными и пассивными клетками. За счет инверсии контроля системы обрабатывают не все клетки автомата, а только возбужденные. Во время своего кванта времени, клетка может перейти в спокойное состояние, остаться в возбужденным или передать возбуждение соседней клетке.
    В терминах ECS это все решается компонентами. Возбужденная клетка игрового поля имеет компонент возбужденности, спокойная - не имеет его. У каждой клетки есть компонент с соседними клетками. Если компонента нет - клетка - это остров.

    И теперь самое интересное. Игровые сущности теперь тоже являются компонентами игрового поля. И за счет все той же инверсии контроля игровые сущности теперь обновляться могут только для возбужденных клеток поля. Если клетка спокойна, стоящий на ней персонаж не получает квант обновления. Это - важное правило, которое требуется соблюдать чтобы не нарушать целостность реализации игрового поля через ECS.
    Любое управление персонажем может только возбуждать клетку игрового поля, где стоит персонаж. Когда персонаж перемещается в другую клетку, между клетками переходит не сам персонаж, а его компоненты. Буквально, сперва делается перемещение данных компонентов персонажа в сущность целевой клетки, а потом происходит удаление компонентов персонажа в текущей клетке.

    Касательно сущностей. Для дискретного поля сущностью является клетка. При этом, согласно терминологии ECS сущность - это эфемерный объект, лишь идентифицирующий свои компоненты. Иными словами, клетка дискретного поля может быть выражена идентификатором своих координат. Запрашивая тот или иной компонент, запуская квант системы или ссылаясь на сущность через идентификатор ее координат, мы всегда будем точно знать геолокацию своих действий в игровом поле. Однако, для реализации такого механизма требуется разработать собственную реализацию ECS.
    Сущностью ECS может быть и не только одна клетка игрового поля, а, скажем, один чанк 16х16 клеток, в котором сохраняются все правила клеточного автомата.

    Через такой подход вполне возможно реализовать что-нибудь вроде Factorio.
    Ответ написан
    Комментировать
  • Инициализация элемента к нулю?

    @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 комментарий