A declaration whose declarator-id is an operator-function-id shall declare a function or function template or an explicit instantiation or specialization of a function template. A function so declared is an operator function.
cout << a.get() << b.get();std::ostream& operator << ( std::ostream&, int ).operator << () стандарт говорит что это перегрузка в форме внешней функции.operator<<( operator<<( cout, a.get() ), b.get() );The evaluations of the postfix expression and of the arguments are all unsequenced relative to one another. All side effects of argument evaluations are sequenced before the function is entered (see 1.9).
If an operator function is invoked using operator notation, argument evaluation is sequenced as specified for the built-in operator; see 16.3.1.2.
cout << a.get() << b.get(); можно упростить до cout << ++i << ++i;, что уже более явно должно показывать наличие UB.2) The value computations (but not the side-effects) of the operands to any operator are sequenced before the value computation of the result of the operator (but not its side-effects).
3) When calling a function (whether or not the function is inline, and whether or not explicit function call syntax is used), every value computation and side effect associated with any argument expression, or with the postfix expression designating the called function, is sequenced before execution of every expression or statement in the body of the called function.
5) The side effect of the built-in pre-increment and pre-decrement operators is sequenced before its value computation (implicit rule due to definition as compound assignment)
16) Every overloaded operator obeys the sequencing rules of the built-in operator it overloads when called using operator notation. (since C++17)
19) In a shift operator expressionE1<<E2andE1>>E2, every value computation and side-effect of E1 is sequenced before every value computation and side effect of E2. (since C++17)
cout << a.get() << b.get(); не определен, но поведение этого кода определено. Поэтому при трансляции по стандарту C++14 этот код может выдать или 12, или 21. Но не 11.cout << a.get() << b.get(); всегда однозначен. При трансляции этого кода по стандарту C++17 (и дальше) в консоль будет выведено всегда и только 12.cout << a.get() << b.get(); не определено.1 имеет категорию prvalue. 1 является литералом и не является строковым литералом. Это - литерал с типом int по умолчанию.const int& ref = 1; или int&& ref = 1; является полностью стандартным.ref в этом случае будет ссылаться на переданный литерал и отсутствие размещения литерала этому не помеха.void foo( const int& ref );
void bar()
{
foo( 1 );
}const int &ref=1; можно найти в том, чтобы не писать магические константы в коде. ref - очень плохое имя. Но голая 1 в коде еще хуже.const &y = x;int. Если в C код const y = x является синтаксически верным и подразумевает const int y = x, то в C++ этот же код является уже синтаксически неверным и не пройдет трансляцию.glMap1[?] имеет довольно узкий коридор поддержки. Она была введена в OpenGL 1.0 и удалена из поддержки в OpenGL 3.2 Core Profile.wglCreateContext, система сама решает какой контекст ей для тебя создавать. Это может быть и контекст с версией 4.6, в котором уже нет поддержки функции glMap1f.wglCreateContextAttribsARB позволяет задавать атрибуты для создаваемого контекста, среди которых ты можешь обозначить и требуемую версию.// Sample code showing how to create a modern OpenGL window and rendering context on Win32.
#include <windows.h>
#include <gl/gl.h>
#include <stdbool.h>
typedef HGLRC WINAPI wglCreateContextAttribsARB_type(HDC hdc, HGLRC hShareContext,
const int *attribList);
wglCreateContextAttribsARB_type *wglCreateContextAttribsARB;
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_create_context.txt for all values
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
typedef BOOL WINAPI wglChoosePixelFormatARB_type(HDC hdc, const int *piAttribIList,
const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);
wglChoosePixelFormatARB_type *wglChoosePixelFormatARB;
// See https://www.khronos.org/registry/OpenGL/extensions/ARB/WGL_ARB_pixel_format.txt for all values
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_FULL_ACCELERATION_ARB 0x2027
#define WGL_TYPE_RGBA_ARB 0x202B
static void
fatal_error(char *msg)
{
MessageBoxA(NULL, msg, "Error", MB_OK | MB_ICONEXCLAMATION);
exit(EXIT_FAILURE);
}
static void
init_opengl_extensions(void)
{
// Before we can load extensions, we need a dummy OpenGL context, created using a dummy window.
// We use a dummy window because you can only set the pixel format for a window once. For the
// real window, we want to use wglChoosePixelFormatARB (so we can potentially specify options
// that aren't available in PIXELFORMATDESCRIPTOR), but we can't load and use that before we
// have a context.
WNDCLASSA window_class = {
.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
.lpfnWndProc = DefWindowProcA,
.hInstance = GetModuleHandle(0),
.lpszClassName = "Dummy_WGL_djuasiodwa",
};
if (!RegisterClassA(&window_class)) {
fatal_error("Failed to register dummy OpenGL window.");
}
HWND dummy_window = CreateWindowExA(
0,
window_class.lpszClassName,
"Dummy OpenGL Window",
0,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0,
0,
window_class.hInstance,
0);
if (!dummy_window) {
fatal_error("Failed to create dummy OpenGL window.");
}
HDC dummy_dc = GetDC(dummy_window);
PIXELFORMATDESCRIPTOR pfd = {
.nSize = sizeof(pfd),
.nVersion = 1,
.iPixelType = PFD_TYPE_RGBA,
.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
.cColorBits = 32,
.cAlphaBits = 8,
.iLayerType = PFD_MAIN_PLANE,
.cDepthBits = 24,
.cStencilBits = 8,
};
int pixel_format = ChoosePixelFormat(dummy_dc, &pfd);
if (!pixel_format) {
fatal_error("Failed to find a suitable pixel format.");
}
if (!SetPixelFormat(dummy_dc, pixel_format, &pfd)) {
fatal_error("Failed to set the pixel format.");
}
HGLRC dummy_context = wglCreateContext(dummy_dc);
if (!dummy_context) {
fatal_error("Failed to create a dummy OpenGL rendering context.");
}
if (!wglMakeCurrent(dummy_dc, dummy_context)) {
fatal_error("Failed to activate dummy OpenGL rendering context.");
}
wglCreateContextAttribsARB = (wglCreateContextAttribsARB_type*)wglGetProcAddress(
"wglCreateContextAttribsARB");
wglChoosePixelFormatARB = (wglChoosePixelFormatARB_type*)wglGetProcAddress(
"wglChoosePixelFormatARB");
wglMakeCurrent(dummy_dc, 0);
wglDeleteContext(dummy_context);
ReleaseDC(dummy_window, dummy_dc);
DestroyWindow(dummy_window);
}
static HGLRC
init_opengl(HDC real_dc)
{
init_opengl_extensions();
// Now we can choose a pixel format the modern way, using wglChoosePixelFormatARB.
int pixel_format_attribs[] = {
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
WGL_COLOR_BITS_ARB, 32,
WGL_DEPTH_BITS_ARB, 24,
WGL_STENCIL_BITS_ARB, 8,
0
};
int pixel_format;
UINT num_formats;
wglChoosePixelFormatARB(real_dc, pixel_format_attribs, 0, 1, &pixel_format, &num_formats);
if (!num_formats) {
fatal_error("Failed to set the OpenGL 3.3 pixel format.");
}
PIXELFORMATDESCRIPTOR pfd;
DescribePixelFormat(real_dc, pixel_format, sizeof(pfd), &pfd);
if (!SetPixelFormat(real_dc, pixel_format, &pfd)) {
fatal_error("Failed to set the OpenGL 3.3 pixel format.");
}
// Specify that we want to create an OpenGL 3.3 core profile context
int gl33_attribs[] = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
0,
};
HGLRC gl33_context = wglCreateContextAttribsARB(real_dc, 0, gl33_attribs);
if (!gl33_context) {
fatal_error("Failed to create OpenGL 3.3 context.");
}
if (!wglMakeCurrent(real_dc, gl33_context)) {
fatal_error("Failed to activate OpenGL 3.3 rendering context.");
}
return gl33_context;
}
static LRESULT CALLBACK
window_callback(HWND window, UINT msg, WPARAM wparam, LPARAM lparam)
{
LRESULT result = 0;
switch (msg) {
case WM_CLOSE:
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
result = DefWindowProcA(window, msg, wparam, lparam);
break;
}
return result;
}
static HWND
create_window(HINSTANCE inst)
{
WNDCLASSA window_class = {
.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
.lpfnWndProc = window_callback,
.hInstance = inst,
.hCursor = LoadCursor(0, IDC_ARROW),
.hbrBackground = 0,
.lpszClassName = "WGL_fdjhsklf",
};
if (!RegisterClassA(&window_class)) {
fatal_error("Failed to register window.");
}
// Specify a desired width and height, then adjust the rect so the window's client area will be
// that size.
RECT rect = {
.right = 1024,
.bottom = 576,
};
DWORD window_style = WS_OVERLAPPEDWINDOW;
AdjustWindowRect(&rect, window_style, false);
HWND window = CreateWindowExA(
0,
window_class.lpszClassName,
"OpenGL",
window_style,
CW_USEDEFAULT,
CW_USEDEFAULT,
rect.right - rect.left,
rect.bottom - rect.top,
0,
0,
inst,
0);
if (!window) {
fatal_error("Failed to create window.");
}
return window;
}
int WINAPI
WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmd_line, int show)
{
HWND window = create_window(inst);
HDC gldc = GetDC(window);
HGLRC glrc = init_opengl(gldc);
ShowWindow(window, show);
UpdateWindow(window);
bool running = true;
while (running) {
MSG msg;
while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT) {
running = false;
} else {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
}
glClearColor(1.0f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Do OpenGL rendering here
SwapBuffers(gldc);
}
return 0;
}f5 не просто первично объявлена, она и определена по месту объявления дружественности. Ее имя является однозначно неквалифицированным в следствии своего определения.f5 не находится.f5(5); буквально обречен на ошибку трансляции.f5 доступен только способ с повторным объявлением, т.к. она уже определена.f5 окончательно потеряет всякий смысл быть дружественной для A и станет просто сильно спрятанной и сбивающей с толку глобальной функцией.f5 относительно A не пользуется, т.к. среди ее параметров нет ни одного с типом A.f5, среди ее параметров обязан быть параметр и с типом A.f5, ее надо дополнительно объявить за пределами типа A в его пространстве имен. 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.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 должны предусмотреть. struct Vector3i final
{
int32_t x;
int32_t y;
int32_t z;
};LineIterator. Такое имя будет хорошо отражать функциональность.using AxisPointer = int32_t Vector3i::*;
class AxisProxy final
{
public:
AxisProxy( AxisPointer major_axis, AxisPointer middle_axis, AxisPointer minor_axis)
: m_major_axis{ major_axis }
, m_middle_axis{ middle_axis }
, m_minor_axis{ minor_axis }
{}
public:
inline int32_t& AccessMajorAxis( Vector3i& value ) const { return value.*m_major_axis; };
inline int32_t& AccessMiddleAxis( Vector3i& value ) const { return value.*m_middle_axis; };
inline int32_t& AccessMinorAxis( Vector3i& value ) const { return value.*m_minor_axis; };
inline const int32_t& AccessMajorAxis( const Vector3i& value ) const { return value.*m_major_axis; };
inline const int32_t& AccessMiddleAxis( const Vector3i& value ) const { return value.*m_middle_axis; };
inline const int32_t& AccessMinorAxis( const Vector3i& value ) const { return value.*m_minor_axis; };
private:
AxisPointer m_major_axis = &Vector3i::x;
AxisPointer m_middle_axis = &Vector3i::y;
AxisPointer m_minor_axis = &Vector3i::z;
};class LineIterator final
{
public:
LineIterator( const Vector3i from, const Vector3i to );
public:
const Vector3i& operator ++ ();
const Vector3i operator ++ ( int );
inline const Vector3i& operator * () const { return m_current_point; };
inline const Vector3i* operator -> () const { return &m_current_point; };
private:
static inline const int32_t GetCorrectionStepAxis( const int32_t value ) { return std::abs( value ) << 1; };
static inline const int32_t GetShiftStepAxis( const int32_t value ) { return ( value > 0 ) - ( value < 0 ); };
void PerformLineStep();
private:
Vector3i m_current_point; // Current position at line.
Vector3i m_correction_step; // Values to change the point corrections.
Vector3i m_shift_step; // The shift step for current point in each iteration.
int32_t m_middle_axis_correction; // The marker for middle axis correction.
int32_t m_minor_axis_correction; // The marker for minor axis correction.
AxisProxy m_axis_proxy; // Point fields proxy.
};const Vector3i& LineIterator::operator ++ ()
{
PerformLineStep();
return m_current_point;
}
const Vector3i LineIterator::operator ++ ( int )
{
Vector3i current_point{ m_current_point };
PerformLineStep();
return current_point;
}LineIterator::LineIterator( const Vector3i from, const Vector3i to )
: m_current_point{ from }
{
const Vector3i line_delta{ to - from };
m_correction_step = { GetCorrectionStepAxis( line_delta.x ), GetCorrectionStepAxis( line_delta.y ), GetCorrectionStepAxis( line_delta.z ) };
m_shift_step = { GetShiftStepAxis( line_delta.x ), GetShiftStepAxis( line_delta.y ), GetShiftStepAxis( line_delta.z ) };
AxisPointer axis[3] = { &Vector3i::x, &Vector3i::y, &Vector3i::z };
std::sort(
std::begin( axis ), std::end( axis ),
[this]( const AxisPointer left, const AxisPointer right ) -> bool
{
return m_correction_step.*left > m_correction_step.*right;
}
);
m_axis_proxy = { axis[0], axis[1], axis[2] };
m_middle_axis_correction = m_axis_proxy.AccessMiddleAxis( m_correction_step ) - ( m_axis_proxy.AccessMajorAxis( m_correction_step ) >> 1 );
m_minor_axis_correction = m_axis_proxy.AccessMinorAxis( m_correction_step ) - ( m_axis_proxy.AccessMajorAxis( m_correction_step ) >> 1 );
}m_correction_step, на базе которого далее производится сортировка указателей на поля осей вектора. Сортируются указатели по убыванию шага их приращения. Именно таким образом определяется главное и вторичное направления. Порядок указателей осей используется для инициализации экземпляра AxisProxy.void LineIterator::PerformLineStep()
{
if( m_middle_axis_correction > 0 )
{
m_middle_axis_correction -= m_axis_proxy.AccessMajorAxis( m_correction_step );
m_axis_proxy.AccessMiddleAxis( m_current_point ) += m_axis_proxy.AccessMiddleAxis( m_shift_step );
}
if( m_minor_axis_correction > 0 )
{
m_minor_axis_correction -= m_axis_proxy.AccessMajorAxis( m_correction_step );
m_axis_proxy.AccessMinorAxis( m_current_point ) += m_axis_proxy.AccessMinorAxis( m_shift_step );
}
m_middle_axis_correction += m_axis_proxy.AccessMiddleAxis( m_correction_step );
m_minor_axis_correction += m_axis_proxy.AccessMinorAxis( m_correction_step );
m_axis_proxy.AccessMajorAxis( m_current_point ) += m_axis_proxy.AccessMajorAxis( m_shift_step );
}AxisProxy::m_middle_axis из кода и удалить весь код, где на него есть ссылки, то весь оставшийся код будет представлять из себя обычный итератор линии для 2D-пространства. В этом случае оси даже сортировать не надо будет, для инициализации прокси можно будет обойтись одним тернарным оператором.