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, т.к. правильно реализован и является частью интерфейса соответствующего типа. The requirement that additional types defined in this section end in "_t" was prompted by the problem of name space pollution. It is difficult to define a type (where that type is not one defined by POSIX.1-2008) in one header file and use it in another without adding symbols to the name space of the program. To allow implementors to provide their own types, all conforming applications are required to avoid symbols ending in "_t", which permits the implementor to provide additional types. Because a major use of types is in the definition of structure members, which can (and in many cases must) be added to the structures defined in POSIX.1-2008, the need for additional types is compelling.
template< typename U, typename... args >
void mapClasses()
{
// ...
if constexpr( sizeof...( args ) > 0 ) // Все понятно и без слов.
{
mapClasses<args...>();
}
}
template< typename... args >
struct Map;
template< typename U >
struct Map<U>
{
static inline void mapClasses()
{
// ...
}
};
template< typename U, typename... args >
struct Map<U, args...>
{
static inline void mapClasses()
{
Map<U>::mapClasses();
Map<args...>::mapClasses();
}
};
template< typename U >
void mapClasses()
{
// ...
}
// SFINAE тут (аргумент функции) выключит вывод шаблона при пустом списке полей.
// В этом случае доступным остается только верхний экземпляр функции.
template< typename U, typename... args >
void mapClasses( char (*)[ sizeof...( args ) > 0 ] = 0 )
{
mapClasses<U>();
mapClasses<args...>();
}