glutIdleFunc
. В документации написано что GLUT будет постоянно вызывать переданную функцию в свободное от обработки системных сообщений время. А если в glutIdleFunc
передан nullptr
(или idle-функция не была задана), то GLUT будет строго ждать следующего системного сообщения.glutPostRedisplay
, в glutIdleFunc
приведет и к регулярной перерисовке экрана.operator()
.int N = 0;
bool GreatherThanN(int x) {
if (x > N) {
return true;
}
return false;
}
template< int N >
bool GreatherThanN(int x) {
if (x > N) {
return true;
}
return false;
}
N
в этом случае надо знать на этапе компиляции. Да и в целом, не всё можно определить через параметры шаблона. Параметром шаблона может быть или тип, или константа целочисленного типа или типа сводимого к целочисленному (enum, bool, const char*, известные на этапе компиляции указатели).operator()
.struct Comparator final
{
int N;
inline const bool operator () ( int x ) const
{
return x > N;
}
};
// ...
Comparator GreatherThanN{ 10 };
// ...
count_if(begin(v), end(v), GreatherThanN);
#include <iostream>
using namespace std;
const char ssid[] = "TEST";
const char password[] = "123456";
struct Wifi {
const char *access[2][2];
} wifi{
{
{ ssid, password },
{ "DIR", "654321" }
}
};
int main() {
for (int i = 0; i < sizeof(wifi.access) / sizeof(wifi.access[0]); i++) {
cout << wifi.access[i][0] << ": " << wifi.access[i][1] << '\n';
// TEST: 123456
// DIR: 654321
}
return 0;
}
В общем выравниваются в памяти поля по границе кратной своему же размеру. То есть 1-байтовые поля не выравниваются, 2-байтовые — выравниваются на чётные позиции, 4-байтовые — на позиции кратные четырём и т.д.
GetDeterminant()
имеет разное поведение для размерности 2 и для остальных размерностей.// Deduction branch. Common determinant calculation.
template< typename TStorage, size_t DIMENSION >
struct DeterminantCalculator final
{
// Or implement the private instantiation for `DeterminantCalculator<TStorage, 1>`.
static_assert( DIMENSION > 2, "Dimension of matrix should be >= 2." );
static inline const TStorage Calculate( const MatrixNxN<TStorage, DIMENSION>& matrix )
{
TStorage result = 0;
for( size_t index = 0; index < DIMENSION; ++index )
{
// ((index & 0)? 1 : -1) --> always -1.
// matrix.GetMinor( 0, 0 ) --> uninitialized result.
// matrix.GetMinor( DIMENSION - 1, 0 ) --> garbage result due to design of `GetMinor`.
result += matrix[ index ] * ((index & 0)? 1 : -1) * matrix.GetMinor( index, 0 ).GetDeterminant();
}
return result;
}
};
// Terminal branch. Determinant for 2x2 matrix.
template< typename TStorage >
struct DeterminantCalculator<TStorage, 2> final
{
static inline const TStorage Calculate( const MatrixNxN<TStorage, 2>& matrix )
{
return matrix[ 0 ] * matrix[ 3 ] - matrix[ 2 ] * matrix[ 1 ];
}
};
template<typename _Type, size_t _Size>
_Type MatrixNxN<_Type, _Size>::GetDeterminant()
{
return DeterminantCalculator<_Type, _Size>::Calculate( *this );
}
template<typename _Type, size_t _Size>
MatrixNxN<_Type, _Size - 1> MatrixNxN<_Type, _Size>::GetMinor(size_t row, size_t col)
{
size_t index = 0;
MatrixNxN<_Type, _Size - 1> result;
// Should be `i < _Size - 1`, since the `result` is `MatrixNxN<_Type, _Size - 1>`.
for (size_t i = 0; i < _Size; i++)
{
if (i == col)
{
// Uninitialized `result` in case of `col == 0`. E.g. ALWAYS.
continue;
}
// Should be `i < _Size - 1`, since the `result` is `MatrixNxN<_Type, _Size - 1>`.
for (size_t j = 0; j < _Size; j++)
{
if (j == row)
{
// Uninitialized `result` in case of `row == 0`.
continue;
}
result[index++] = GetElement(j, i);
}
}
return result;
}
swap
и посмотреть на то, как компилятор будет генерировать код даже с учетом оптимизации.void swap( int& left, int& right )
{
static int t = left;
left = right;
right = t;
}
swap(int&, int&): # @swap(int&, int&)
pushq %r14
pushq %rbx
pushq %rax
movq %rsi, %rbx
movq %rdi, %r14
movb guard variable for swap(int&, int&)::t(%rip), %al
testb %al, %al
je .LBB0_1
.LBB0_3:
movl (%rbx), %eax
movl %eax, (%r14)
movl swap(int&, int&)::t(%rip), %eax
movl %eax, (%rbx)
addq $8, %rsp
popq %rbx
popq %r14
retq
.LBB0_1:
movl $guard variable for swap(int&, int&)::t, %edi
callq __cxa_guard_acquire
testl %eax, %eax
je .LBB0_3
movl (%r14), %eax
movl %eax, swap(int&, int&)::t(%rip)
movl $guard variable for swap(int&, int&)::t, %edi
callq __cxa_guard_release
jmp .LBB0_3
void swap( int& left, int& right )
{
int t = left;
left = right;
right = t;
}
swap(int&, int&): # @swap(int&, int&)
movl (%rdi), %eax
movl (%rsi), %ecx
movl %ecx, (%rdi)
movl %eax, (%rsi)
retq
t
объявить именно как глобальную переменную, то сгенерированный код станет немного легче, но он все равно будет объемнее и тяжелее кода с локальной переменной. Уже просто потому что глобальная переменная находится в глобальной памяти, а запросы из процессора в ОЗУ - сравнительно долгая операция.this->value = NULL;
NULL
ты надеешься на то что это будет указатель. Далее по тексту ты сам же свою надежду и разбиваешь, инстанцируя свой шаблон от типа int
.this->value = T1(); // Или this->value = T1{}; для C++11 и выше.
this
в этом месте, кстати, можно смело опустить.Type(T1 value) : TypeSize(value)
// ...
Type() : TypeSize()
Type(T1 value) : TypeSize<T1>::TypeSize(value)
// ...
Type() : TypeSize<T1>::TypeSize()
this->value
на value
, то компилятор снова скажет про обращение к несуществующему полю. Но если к value
обращаться используя его полную квалификацию TypeSize<T1>::value
, код соберется должным образом.Type(T1 value) : TypeSize(value)
{
this->value = value;
}
Type() : TypeSize()
{
this->value = 0;
}
value
из этих конструкторов лучше убрать и предоставить все права по инициализации своих полей типу TypeSize
. Плюс, конструктор по умолчанию у тебя снова надеется на простой тривиальный тип. Присвоение нулю приведет к ошибке компиляции уже при инстанцировании для тривиального составного типа.Type(T1 value) : TypeSize<T1>::TypeSize(value)
{
}
Type() : TypeSize<T1>::TypeSize()
{
}
template< typename TValueType >
struct Serializer final
{
static inline const bool Read( const BasicStream& stream, TValueType& value_storage );
static inline const bool Write( BasicStream& stream, const TValueType& value_storage );
};
template<>
struct Serializer<int8_t> final
{
static inline const bool Read( const BasicStream& stream, int8_t& value_storage );
static inline const bool Write( BasicStream& stream, const int8_t& value_storage );
};
// ...
template<>
struct Serializer<float> final
{
static inline const bool Read( const BasicStream& stream, float& value_storage );
static inline const bool Write( BasicStream& stream, const float& value_storage );
};
std::string
, и std::vector
, и std::map
. Если пользователь решит сериализовать весь контейнер своих пользовательских типов, нам не стоит обязывать его писать частное инстанцирование сериализатора для контейнера. Нам надо предусмотреть сериализацию любого стандартного контейнера с любым пользовательским типом.template< typename TCharType, typename TCharTraits, typename TAllocator >
struct Serializer<std::basic_string<TCharType, TCharTraits, TAllocator>> final
{
static inline const bool Read( const BasicStream& stream, std::basic_string<TCharType, TCharTraits, TAllocator>& value_storage );
static inline const bool Write( BasicStream& stream, const std::basic_string<TCharType, TCharTraits, TAllocator>& value_storage );
};
template< typename TElement, typename TAllocator >
struct Serializer<std::vector<TElement, TAllocator>> final
{
static inline const bool Read( const BasicStream& stream, std::vector<TElement, TAllocator>& value_storage );
static inline const bool Write( BasicStream& stream, const std::vector<TElement, TAllocator>& value_storage );
};
template< typename TLeftElement, typename TRightElement >
struct Serializer<std::pair<TLeftElement, TRightElement>> final
{
static inline const bool Read( const BasicStream& stream, std::pair<TLeftElement, TRightElement>& value_storage );
static inline const bool Write( BasicStream& stream, const std::pair<TLeftElement, TRightElement>& value_storage );
};
template< typename TElement, typename TAllocator >
inline const bool Serializer<std::vector<TElement, TAllocator>>::Read(
const BasicStream& stream,
std::vector<TElement, TAllocator>& value_storage
)
{
size32_t stored_size;
Serializer<size32_t>::Read( stream, stored_size );
value_storage.resize( stored_size );
for( TElement& element : value_storage )
{
Serializer<TElement>::Read( stream, element );
}
return true;
}
К примеру есть файл .cpp, в нем есть метод, который не относится не к какому пространству
Есть класс, он описан и тд, но я заметил в нем есть "вызова" методов, без всякого описания, то есть даже значения не передаются
size_t min_value_index = 0;
size_t max_value_index = 0;
0
для того чтобы изначально обозначить первый же элемент массива и как минимальный, и как максимальный одновременно. Именно так я определю начальное состояние алгоритма.for( size_t index = 1; index < stream_length; ++index )
{
// ...
}
for( size_t index = 1; index < stream_length; ++index )
{
if( stream[ index ] < stream[ min_value_index ] )
{
min_value_index = index;
}
if( stream[ index ] > stream[ max_value_index ] )
{
max_value_index = index;
}
}
min_value_index
будет гарантированно лежать индекс минимального значения массива, а в max_value_index
- индекс максимального.(arg / N)
. Что это означает? Это означает просто целочисленное деление.arg
будет эквивалентен N
.(int)CycleIteration
. Опустим момент что писавший это человек любит "острые ощущения". Обратим внимание только на то, что тут адрес глобальной функции переводится в число.((int)CycleEnd - (int)CycleIteration))
. Тип int
тут не спроста. Функции CycleEnd
и CycleIteration
могут быть расположены в произвольных местах бинарного образа. Тут определяется буквальное расстояние между двумя функциями в образе. И это расстояние может быть как положительным, так и отрицательным.ptr
будет принимать значение CycleIteration
до тех пор, пока arg < N
, а когда arg == N
, ptr
принимает значение CycleEnd
. TEST( TestStaticString, GetChar )
{
using TestString = Black::StaticString<'A', 'B', 'C'>;
EXPECT_EQ( 'A', TestString::GetChar( 0 ) );
EXPECT_EQ( 'B', TestString::GetChar( 1 ) );
EXPECT_EQ( 'C', TestString::GetChar( 2 ) );
}
TEST
- это макрос, раскрывающийся в специальную обвязку твоего теста. Его первый параметр - это имя тестового кейса, второй - имя теста.EXPECT_*
и ASSERT_*
.VectorTest
- это фикстура тестов. Применять ее к своим тестам ты можешь с помощью макроса TEST_F
. Прочитать про это можно тут.Еще один момент, который нужно отметить: использование std::remove_reference. На самом деле forward может быть реализован и без использования этой функции. Сжатие ссылок выполнит всю работу, таким образом, применение std::remove_reference для этого избыточно. Однако, эта функция позволяет вывести T& t в ситуации, когда этот тип не может быть выведен (согласно стандарту С++, 14.8.2.5), поэтому необходимо явно указывать параметры шаблона при вызове std::forward.
swap()
делает размен состояния между двумя контейнерами.cache
. При размене не происходит дополнительных выделений памяти, состояния векторов в буквальном смысле меняются местами.cache
с набором из -1
.template< typename TInterface, typename... TArguments >
class AbstractFactory final
{
public:
// Produce the implementation, but return the pointer to interface.
inline std::shared_ptr<TInterface> Produce( const std::string& implementation_name, TArguments... arguments )
{
auto found_function = m_factory_functions.find( implementation_name );
return ( found_function == m_factory_functions.end() )? std::shared_ptr<TInterface>{} : found_function->second( std::forward<TArguments>( arguments )... );
};
// Define the implementation.
template< typename TImplementation >
inline const bool DefineImplementation()
{
return DefineImplementation<TImplementation>( TImplementation::ClassName() );
};
// Define the implementation.
template< typename TImplementation >
inline const bool DefineImplementation( const std::string& implementation_name )
{
// Abort the incorrect registration.
static_assert( std::is_base_of<TInterface, TImplementation>::value, "Implementation may only be derived from interface of Factory." );
auto found_function = m_factory_functions.find( implementation_name );
if( found_function == m_factory_functions.end() )
{
m_factory_functions[ implementation_name ] = &AbstractFactory<TInterface, TArguments...>::template ConstructImplementation<TImplementation>;
return true;
};
return false;
};
// Check the implementation name is already defined.
inline const bool IsImplementationDefined( const std::string& implementation_name ) const
{
return m_factory_functions.find( implementation_name ) != m_factory_functions.end();
};
private:
// The factory function just produce implementation.
template< typename TImplementation >
static std::shared_ptr<TInterface> ConstructImplementation( TArguments... arguments )
{
return std::static_pointer_cast<TInterface>(
std::make_shared<TImplementation>( std::forward<TArguments>( arguments )... )
);
};
private:
// Factory function produces the implementations of TInterface.
using FactoryFunction = std::shared_ptr<TInterface> (*)( TArguments... arguments );
std::unordered_map<std::string, FactoryFunction> m_factory_functions;
};
WCHAR
зависит от настроек проекта и может быть как псевдонимом char
, так и wchar_t
. Широкое использование этого типа во всем проекте не рекомендуется, т.к. повсеместно вносит неопределенность.std::wstring
, std::u16string
и std::u32string
.std::wstring
хранит символы типа wchar_t
, который, в зависимости от настроек компилятора, может занимать 2 или 4 бйта. Это делает тип std::wstring
столь же неоднозначным, как и WCHAR
. Поэтому и были созданы типы со строгим размером символа: std::u16string
и std::u32string
. Сейчас рекомендуется пользоваться ими вместе с std::string
.std::transform
и лямбды, а в преобразовании однобайтовой (std::string) кодировки в Unicode и обратно.template< typename TCharType, typename TCharTraits, typename TStringAllocator >
inline void Convert( const std::string& source_string, std::basic_string<TCharType, TCharTraits, TStringAllocator>& dest_string )
{
std::wstring_convert<std::codecvt_utf8_utf16<TCharType>, TCharType> converter;
dest_string = converter.from_bytes( source_string );
}
template< typename TCharType, typename TCharTraits, typename TStringAllocator >
inline void Convert( const std::basic_string<TCharType, TCharTraits, TStringAllocator>& source_string, std::string& dest_string )
{
std::wstring_convert<std::codecvt_utf8_utf16<TCharType>, TCharType> converter;
dest_string = converter.to_bytes( source_string );
}
Because of argument-dependent lookup, non-member functions and non-member operators defined in the same namespace as a class are considered part of the public interface of that class (if they are found through ADL)
operator>>
никакими магическими силами не переносится в глобальное пространство имен. Он просто находится по ADL, т.к. правильно реализован и является частью интерфейса соответствующего типа.