layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aOffset;
aOffset
не должен быть uniform-ом. Это должен быть именно атрибут инстанса.#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aOffset;
void main()
{
gl_Position = vec4(aPos + aOffset, 0.0, 1.0);
}
glVertexAttribDivisor
[?].glVertexAttribDivisor( 1, 6 );
.glm::vec2 translations[100]
.glm::vec2 translations[100];
int index = 0;
float offset = 0.1f;
for (int y = -10; y < 10; y += 2)
{
for (int x = -10; x < 10; x += 2)
{
glm::vec2 translation;
translation.x = (float)x / 10.0f + offset;
translation.y = (float)y / 10.0f + offset;
translations[index++] = translation;
}
}
glBindBuffer(GL_ARRAY_BUFFER, VBOs[3]);
glBufferData(GL_ARRAY_BUFFER, sizeof(translations), translations, GL_STATIC_DRAW);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(glm::vec2), (GLvoid*)0);
glVertexAttribDivisor(1, 6);
glEnableVertexAttribArray(1);
int
правильно выбран в качестве типа для result
.x + y
работает только она.true
всегда в таких случаях будет преобразован в 1
с типом int
.x + y
типом результата значения будет именно int
, потому что ранг short int
ниже ранга int
в правилах продвижения. Арифметические операции с значениями всех типов, чей ранг ниже int
, всегда подвергаются продвижению и выполняются на значениях с типом int
.auto result
в левой части выражения. Обобщение принимает тип результата выражения справа от знака равенства, коим и является int
.Как я понимаю, эта техника позволяет писать перегрузку оператора внутри класса, как будто это член класса, а не глобальная бинарная дружественная функция.
friend std::ostream& operator<<(std::ostream& os, const Object & r) {/**/ return os;}
operator <<
все так же остается глобальной функцией, определенной в том же пространстве имен, где определен и тип Object
, но не в пространстве имен типа Object
. Но, будучи определенным по месту объявления дружественности, оператор стал только ADL-доступным. Обращение к этому оператору может быть найдено только тогда, когда в конструкции std::cout << obj;
этот obj
имеет тип Object
.Но почему такая же логика не работает с обычной функцией?
friend void fOUT (void) { }
, опять же, является определением по месту объявления дружественности и доступна только через ADL. Но у нее нет аргументов чтобы ADL смог найти ее при обращении. Поэтому такая конструкция является бесполезной. GL_POLYGON
предназначен для вывода только выпуклых многоугольников, а у тебя он выпуклым не является.GL_QUAD
, или в режиме GL_TRIANGLES
, явным образом выделяя отдельные квадратные участки.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
включалась только одна конкретная перегрузка шаблона. .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<<E2
andE1>>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++ этот же код является уже синтаксически неверным и не пройдет трансляцию.