f5
не просто первично объявлена, она и определена по месту объявления дружественности. Ее имя является однозначно неквалифицированным в следствии своего определения.f5
не находится.f5(5);
буквально обречен на ошибку трансляции.f5
доступен только способ с повторным объявлением, т.к. она уже определена.f5
окончательно потеряет всякий смысл быть дружественной для A
и станет просто сильно спрятанной и сбивающей с толку глобальной функцией.f5
относительно A
не пользуется, т.к. среди ее параметров нет ни одного с типом A
.f5
, среди ее параметров обязан быть параметр и с типом A
.f5
, ее надо дополнительно объявить за пределами типа A
в его пространстве имен. GLFWwindow
[?].glfwCreateWindow
[?] невозможно передать и дескриптор окна операционной системы если хочется создать дочернее окно в своем.glfwCreateWindow
, то можно заметить последний параметр, который позволяет указать другое окно, ресурсы которого необходимо разделить с новым создаваемым окном. Используя этот параметр можно создать несколько окон с совместным управлением ресурсами, но рисовать в этих окнах все равно можно лишь в разных потоках.num в данном случае представляет собой шестнадцатеричное число, при выводе оно переводится в десятичное число типа double.
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.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?
auto
и инициализацией списками, понимание цены отказа от std::tie
в пользу структурного связывания (не говоря уже о понимании семантики структурного связывания) и понимание рациональности перехода на структурное связывание в конкретном месте кода, понимание последствий использования автоматического выведения аргументов шаблона типа в месте вызова конструктора и возможные последствия применения этого подхода в конкретном участке кода, понимание семантики работы с this
в замыкании лямбды и отличия этого поведения в более старых стандартах. И так далее. template< typename... TArguments >
void Foo( const TArguments&... arguments )
{
static_assert( sizeof...( TArguments ) > 0, "This function shall not be called without arguments." );
}
glTexImage2D
.data
памяти меньше расчетного размера, который определяет функция исходя из параметров width, height, GL_RGBA, GL_UNSIGNED_BYTE
.glTexImage2D
параметры не соответствуют считанному изображению. А т.к. width
и height
получены напрямую при чтении изображения, вопросы возникать могут только относительно заявленного формата изображения - GL_RGBA, GL_UNSIGNED_BYTE
.Book
является агрегатным.auto
вместо явного указания типа переменной? Зачем писателю потребовалось дважды указать тип, заменив первое указание на auto
? Какова цель инициализации копией для данной переменной? Зачем писатель написал так сложно?entt::registry::view
.ColorComponent
, и этот цвет у тебя может интерполироваться. Значит тебе нужен компонент интерполируемости цвета - ColorInterpolationComponent
. В этом компоненте интерполируемости нужно определить закон интерполяции, начальное и конечное значение, а так же время интерполяции. ColorInterpolationComponent
говорит о том, что все данные ColorComponent
интерполируются по определенному закону между определенными значениями и за определенное время.***InterpolationComponent
у тебя будет своя система, которая делает выборку по целевому компоненту и по компоненту интерполируемости. Таким образом, уже на стадии выборки компонентов у тебя останутся только подходящие сущности, а все остальные - отфильтруются.int
.VkResult result
можно написать int result
в виду того, что перечисление VkResult
является нестрогим.C26812
[?] относится к разделу Enum.3 из C++ Core Guidelines [?] и напрямую указывает на место определения нестрогого перечисления. К строке VkResult result =
оно отношения не имеет.#pragma warning(push, 0)
[?] не делает того, что ты от него ожидаешь.#pragma warning( push )
#pragma warning( disable: 26812 )
#include <GLFW/glfw3.h>
#pragma warning( pop )
const
относится к правой половине спецификации типа до первого модификатора.const char&
- ссылка на константный символ. Квалификатор - const
, модификатор - &
.const char*
- указатель на память константного символа. Квалификатор - const
, модификатор - *
.const
относится ко всей части спецификации типа левее, включая все модификаторы.char* const
- константный указатель на память символа.const char* const
- константный указатель на память константного символа.char* const *
- указатель на память константного указателя на память символа.char& const
существовать не может, т.к. квалификаторы не применяется к ссылкам. Тут будет ошибка трансляции.constexpr
? Просто constexpr
всегда относится только ко всей спецификации типа со всеми модификаторами.const char*
- указатель на память константного символа.constexpr char*
- константный указатель времени компиляции на память символа. Тут нет ошибки, память символа тут считается модифицируемой.constexpr char*
получит характеристику ODR-used [?], то после трансляции кода это будет уже объект с типом char* const
. Вот так.const char[N]
, т.е. статически определенный массив константных символов. Такой тип можно привести только к типу const char*
.constexpr const char*
.const
в этом месте никакого прямого отношения к constexpr
не имеет. template< typename TComponent >
TComponent* const QueryComponent();
void PerformWorkflow( A*, B*, C* );
A
, B
и C
- это типы компонентов.template< typename... TComponents >
void ApplyComponents( void (*workflow)( TComponents*... ) )
{
workflow( QueryComponent<TComponents>()... );
}
each
и типа результата get
. Стоит напомнить что разыменование nullptr
, который может вернуть get
, приводит к неопределенному поведению [?].*QueryComponent<TComponents>()...
, т.е. на лету разыменовывать полученные указатели, то перед запуском функции для твоей entity
следует убедиться что у нее есть все необходимые компоненты. Такая проверка является необходимой для запуска систем в ECS, т.к. система должна обрабатывать только удовлетворяющие своим фильтрам сущности.template <typename... T>
void each(typename identity<std::function<void(T& ...)>>::type func)
{
for(auto &entity: m_entities)
{
if( !( (entity.HasComponent<T>() && ...) ) )
{
continue;
}
func( *entity.get<T>()... );
}
}
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 должны предусмотреть. К примеру есть сущность игрок с набором компонентов наследованная от Entity,
Про состояния, пустые компоненты добавлять удалять в entity как флаги что сущность в каком либо состоянии, и отдельные системы для обработки каждого состояния это нормальный подход ?!