MyClass.h и MyClass.cpp. Когда нужно тематически разделить определение интерфейса, к имени класса после точки и перед расширением файла добавляется суффикс, говорящий о тематике определения. Например MyClass.serialization.cpp, MyClass.crud.cpp или MyClass.callbacks.cpp.template< size_t ROWS, size_t COLUMNS >
struct Matrix final
{
template< size_t R = ROWS, size_t C = COLUMNS >
inline std::enable_if_t<R == C, int> GetDeterminant() const
{
return 0;
}
};GetDeterminant не удалось вывести из шаблона? Будет ошибка трансляции, говорящая о том, что метод не найден. Это ничего не говорит пользователю. Это просто инструктирует транслятор остановить трансляцию. Пользователь, особенно если он не искушен знаниями о SFINAE, не сможет понять причину ошибки трансляции. Такая ситуация создаст риск излишней траты времени на дознание причин ошибки.template< size_t ROWS, size_t COLUMNS >
struct Matrix final
{
template< size_t R = ROWS, size_t C = COLUMNS >
inline std::enable_if_t<R != C, int> GetDeterminant() const = delete;
template< size_t R = ROWS, size_t C = COLUMNS >
inline std::enable_if_t<R == C, int> GetDeterminant() const
{
return 0;
}
};GetDeterminant.template< size_t ROWS, size_t COLUMNS >
struct Matrix final
{
inline int GetDeterminant() const
{
static_assert( ROWS == COLUMNS, "Matrix should be square to calculate the determinant." );
return 0;
}
};ArrayArray[0] = *new Array<int>{10};ArrayArray[0] вернет ссылку на Array<int>.*new Array<int>{10} выделяет память в куче под Array<int>, вызывает инициализатор Array<int>::Array(int length), после чего делает разыменование получившегося указателя на Array<int>.Array<int> будет выполнен оператор копирования по умолчанию, функциональность которого просто скопирует поля из объекта справа в объект слева от присвоения.new Array<int>{10} становится утекшим, т.к. указатель на него сразу становится потерян и освободить занимаемую им память становится невозможно.ArrayArray[0] = Array<int>{10};ArrayArray[0] вернет ссылку на Array<int>.Array<int>{10} инициализирует безымянный локальный объект на стеке, используя инициализатор Array<int>::Array(int length).T *m_data.ArrayArray[0] в этот момент начинает ссылаться на освобожденную память.ArrayArray в конце работы программы пытается удалить уже освобожденную память в ArrayArray[0].resize[?].insert[?].vertexBuffer.insert( vertexBuffer.begin() + 1, x );Только хотелось бы без специализации, чтобы код не дублировать
std::enable_if[?]. Часто его используют для выбора поведения шаблона исходя из черт аргумента шаблона.соответственно во всех методах, где этот член используется можно будет проверку if constexpr (someCondition)
if constexpr, нужно убирать в функции. А где эти функции лучше расположить? Верно - прямо там, где для их работы определены данные. Поэтому на самом деле if constexpr не нужен. Нужно определить набор функций с поведением там где оно возможно. А где нет - определить заглушки чтобы клиентский, относительно вариативного поведения, код не нуждался в проверках и мог просто обращаться к вариативному поведению так, как будто оно не вариативно и есть всегда.DebugName[?], в котором такие заглушки реализованы. *reinterpret_cast<T*>( &buffer[ sizeof( T ) * head ] ) = T{ std::forward<Args>( args )... };auto T vicar{ std::forward<Args>( args )... };
std::swap( *reinterpret_cast<T*>( &buffer[ sizeof( T ) * head ] ), vicar );std::swap, но только от людей со слабой привычкой пользоваться STL.T является неперемещаемым. В твоем ЦБ должны присутствовать проверки на перемещаемость, копируемость и возможность размена состояний.T можно копировать, но не перемещать, использовать стоит оператор копирования.T нельзя, следует пользоваться деструктором и размещающим конструктором.T включалась только одна конкретная перегрузка шаблона. BitmapData data = bitmap2.LockBits(new System.Drawing.Rectangle(x, y, weight, height),
ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppRgb);data будет XRGB с шириной 32 бита.GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, data.Scan0);PixelInternalFormat.Rgba означает что в памяти GPU текстура будет представлена в формате RGBA с каналами float с нормализацией.OpenTK.Graphics.OpenGL.PixelFormat.Rgba означает что формат data.Scan0 нужно воспринимать как RGBA, а PixelType.UnsignedByte означает что каналы в data.Scan0 нужно воспринимать как unsigned byte.XRGB != RGBA.Format32bppArgb. Но в этом случае есть вероятность ошибиться с порядком каналов между Format32bppArgb и OpenTK.Graphics.OpenGL.PixelFormat.Rgba..cpp обычно являются точками сборки модулей трансляции [?]..cpp подключить другой .cpp (исключенный из сборки иными способами), то все элементы с внутренним связыванием любого из этих .cpp будут доступны в них обоих..h. Файлы .cpp обычно включают .h и все вместе своим кодом формируют модуль трансляции, в котором доступны все элементы с внутренним связыванием. Даже в коде файлов .h.static) элементы именованных пространств имен по умолчанию имеют внешнее связывание [?]. Глобальное пространство является тоже именованным (и доступно через :: без имени слева) и ровно так же наделяет все свои нестатические элементы характеристикой внешнего связывания.inline.inline[?] дает пометку слабого внешнего связывания для любой сущности. Что константа или глобальная переменная, что функция или метод (даже статический), помечаются как сущности с внешним связыванием, которое не нарушает ODR в случае если все определения цели связывания во всех модулях трансляции являются полностью одинаковыми. Если хоть одно определение цели связывания отличается - будет нарушение ODR..cpp определить inline функции с одним именем и одной сигнатурой, но разными телами, то проблем со сборкой будет не избежать.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++ этот же код является уже синтаксически неверным и не пройдет трансляцию.