14. Template metaprogramming, type_traits library

Template metaprogramming

  • template metaprogramming
    • const and constexpr
  • Type determination of type_traits
    • is_same, is_class, is_enum, is_integra, is_floating_point, is_pointer, is_lvalue_reference, is_rvalue_reference, is_function, is_member_function_pointer, is_array, is_arithmetic, is_abstract
    • is_same
    • consultation
      • decltype
    • consultation
    • decay
      • CV qualifier
    • enable_if
    • integral_constant
  • Find the largest integer
  • Find memory alignment
      • alignment_of

Template metaprogramming

Used extensively in oss and mysql
Template metadata cannot be run-time variables, but can only be compile-time constants.
Grammar cannot use if, else, for, etc.
Generally used in conjunction with type redefinition, enumeration, inheritance, template partial specialization, etc.

Prepare before compiling
typedef/using defines source data
enum, static, enumeration and static, const defines the compiler’s integer constant
T, Args… declare source data type
::Analyze type scope and obtain calculation results
template template, defines source data

If else for cannot be used in template metaprogramming. If needed, it can be implemented in other ways.
if and else can be implemented through the ternary operator
for can be implemented through recursion, overloading, and templates (partial specialization)

type_traits library for c++11
Provides a large number of calculations, queries, judgments, conversions, and selections during compilation

const and constexpr

const tells the compiler that during initialization, it cannot be assigned or modified, but the value can be modified through overflow.

constexpr tells the compiler that the current constant expression can be optimized and has more restrictions than const

class.h

#pragma once

template<class T,T v>//pass v as a parameter
struct Fconst1
{<!-- -->
static constexpr T value = v; //Use Fconst1 to create a constant of type T and value v

};

//template<int v1, int v2>//will report an error
//struct Fconst2
//{<!-- -->
// static constexpr T value = v1 + v2;
//
//};

//template<class T1,T1 v1,class T2,T2 v2>//will report an error
//struct fconst2
//{<!-- -->
// static constexpr t value = v1 + v2;
//
//};

template<class T>
struct RemoveConst
{<!-- -->
usingType = T;
};

template<class T>
struct RemoveConst<const T>
{<!-- -->
usingType = T;
};

//Determine whether two types are the same
template<bool v>
using bool_temp = Fconst1<bool, v>;

//The parameters are not equal to go here
template<class, class>
constexpr bool isSame_V = false;
//Two of the same go here
template<class T1>
constexpr bool isSame_V<T1, T1> = true;
//Template specialization is used. When the two types are the same, use constexpr bool isSame_V<T1,T1> = true;
template<class T1,class T2>
struct isSame :bool_temp<isSame_V<T1, T2>> {<!-- -->};

learn.cpp

#include <iostream>
#include "advanced.h"
#include "class.h"

struct FTestA
{<!-- -->

};

struct FTestB
{<!-- -->
FTestB(int a, int b)
{<!-- -->

}
int print(int a, int b)
{<!-- -->
printf("%i %i", a, b);

return a + b;
}
};

int main()
{<!-- -->
/*
FTestA* p = CreateObject<FTestA>();

FTestB* p2 = CreateObject<FTestB>(1,2);

auto NewFunction = CreateDelegate(p2, & amp;FTestB::print);

int a = NewFunction(1, 2);

cout << "\
" << a << endl;
*/

int v = Fconst1<int, 1>::value;
//The Fconst1 template structure is used to pass 1 as the value parameter v to the type parameter T, and then use the value static member variable to obtain this value.

RemoveConst<const int>::Type a1;
//Use RemoveConst template structure to remove const modifier. In this example, the type passed is const int, so using RemoveConst<const int> will return an int type without the const modifier.
a1 = 100;

//int v = Fconst2<1, 2>::value;//An error will be reported

// Compare types to see if they are the same
bool TT = isSame<int, float>::value;
//Use the isSame template structure to determine whether the two types of int and float are the same. In this example, they are different types, so TT evaluates to false

return 0;
}

Type determination of type_traits

is_same、is_class、is_enum、is_integra、is_floating_point、is_pointer、is_lvalue_reference、is_rvalue_reference、is_function、is_member_function_pointer、is_array、is_arithmetic、is_abstract

Are the types the same?
std::is_same compares whether the given types are the same, returns 1 if they are the same, and returns 0 if they are not the same.

kind
std::is_class determines whether the given type is a class type, if so it returns 1, if not it returns 0

enumerate
std::is_enum determines whether the given type is an enumeration type, if so it returns 1, if not it returns 0

integer
std::is_integral determines whether the given type is an integer, if so it returns 1, if not it returns 0

floating point
std::is_floating_point determines whether the given type is a floating point type, if so it returns 1, if not it returns 0

pointer
std::is_pointer determines whether the given type is a pointer, if so it returns 1, if not it returns 0

lvalue
is_lvalue_reference determines whether the given type is an lvalue, if so it returns 1, if not it returns 0

rvalue
is_rvalue_reference determines whether the given type is an rvalue, if so it returns 1, if not it returns 0

function
is_function determines whether the given type is a function, if so it returns 1, if not it returns 0

member function
is_member_function_pointer determines whether the given type is a member function, if so it returns 1, if not it returns 0

container
is_array determines whether the given type is a container, if so it returns 1, if not it returns 0

integer or float
is_arithmetic determines whether the given type is an integer or a floating point type. If it is, it returns 1, if not, it returns 0.

abstract class
is_abstract determines whether the given type is an abstract class, if so it returns 1, if not it returns 0

learn.cpp
(Test case, useless)

#include <iostream>
#include"advanced.h"
#include"class.h"
#include <type_traits>
#include <vector>

struct FTestA
{<!-- -->

};

struct FTestB
{<!-- -->
FTestB(int a, int b)
{<!-- -->

}
int print(int a, int b)
{<!-- -->
printf("%i %i", a, b);

return a + b;
}
};

class A
{<!-- -->
public:
struct C
{<!-- -->
int a;
};
};

enumETestEnum
{<!-- -->
AA,BB,
};

class FInterface
{<!-- -->
public:
virtual void Init() = 0;
};

int main()
{<!-- -->
int v = Fconst1<int, 1>::value;
RemoveConst<const int>::Type a1;
a1 = 100;
bool TT = isSame<int, float>::value;
\t
autoTest1 = []()
{<!-- -->
cout << "Test1" << endl;
};

//std::is_same compares whether the given types are the same, returns 1 if they are the same, and returns 0 if they are not the same.
cout << endl;
cout << std::is_same<int, int>::value << endl;//Output result: 1
cout << endl;

//std::is_class determines whether the given type is a class type, if so it returns 1, if not it returns 0
cout << std::is_class<FTestB>::value << endl;//output result: 1
cout << std::is_class<A>::value << endl;//output result: 1
cout << std::is_class<int>::value << endl;//output result: 0
cout << std::is_class<float>::value << endl;//output result: 0
cout << std::is_class<ETestEnum>::value << endl;//output result: 0//judging that the enumeration is not a class type
cout << endl;

//std::is_enum determines whether the given type is an enumeration type, if it returns 1, if not returns 0
cout << std::is_enum<FTestB>::value << endl;//output result: 0
cout << std::is_enum<A>::value << endl;//output result: 0
cout << std::is_enum<int>::value << endl;//output result: 0
cout << std::is_enum<float>::value << endl;//output result: 0
cout << std::is_enum<ETestEnum>::value << endl;//output result: 1
cout << endl;

//std::is_integral judges whether the given type is an integer, if it returns 1, if not returns 0
cout << std::is_integral<FTestB>::value << endl;//output result: 0
cout << std::is_integral<A>::value << endl;//output result: 0
cout << std::is_integral<int>::value << endl;//output result: 1
cout << std::is_integral<float>::value << endl;//output result: 0
cout << std::is_integral<ETestEnum>::value << endl;//output result: 0
cout << endl;

//std::is_floating_point judges whether the given type is floating-point, if it returns 1, if not returns 0
cout << std::is_floating_point<FTestB>::value << endl;//output result: 0
cout << std::is_floating_point<A>::value << endl;//output result: 0
cout << std::is_floating_point<int>::value << endl;//output result: 0
cout << std::is_floating_point<float>::value << endl;//output result: 1
cout << std::is_floating_point<ETestEnum>::value << endl;//output result: 0
\t
cout << std::is_floating_point<FTestB*>::value << endl;//output result: 0
cout << std::is_floating_point<A*>::value << endl;//output result: 0
cout << std::is_floating_point<int*>::value << endl;//output result: 0
cout << std::is_floating_point<float*>::value << endl;//output result: 0//float type pointer is not
cout << std::is_floating_point<ETestEnum*>::value << endl;//output result: 0
cout << endl;

//std::is_pointer determines whether the given type is a pointer, if it returns 1, if not returns 0
cout << std::is_pointer<FTestB>::value << endl;//output result: 0
cout << std::is_pointer<A>::value << endl;//output result: 0
cout << std::is_pointer<int>::value << endl;//output result: 0
cout << std::is_pointer<float>::value << endl;//output result: 0
cout << std::is_pointer<ETestEnum>::value << endl;//output result: 0

cout << std::is_pointer<FTestB*>::value << endl;//output result: 1
cout << std::is_pointer<A*>::value << endl;//output result: 1
cout << std::is_pointer<int*>::value << endl;//output result: 1
cout << std::is_pointer<float*>::value << endl;//output result: 1
cout << std::is_pointer<ETestEnum*>::value << endl;//output result: 1
cout << endl;

// & amp;lvalue & amp; & amp;rvalue
//is_lvalue_reference determines whether the given type is an lvalue, if it returns 1, if not returns 0
cout << std::is_lvalue_reference<FTestB>::value << endl;//output result: 0
cout << std::is_lvalue_reference<A>::value << endl;//output result: 0
cout << std::is_lvalue_reference<int>::value << endl;//output result: 0
cout << std::is_lvalue_reference<float>::value << endl;//output result: 0
cout << std::is_lvalue_reference<ETestEnum>::value << endl;//output result: 0
\t
cout << std::is_lvalue_reference<FTestB & amp;>::value << endl;//output result: 1
cout << std::is_lvalue_reference<A & amp;>::value << endl;//output result: 1
cout << std::is_lvalue_reference<int & amp;>::value << endl;//Output result: 1
cout << std::is_lvalue_reference<float & amp;>::value << endl;//Output result: 1
cout << std::is_lvalue_reference<ETestEnum & amp;>::value << endl;//Output result: 1

cout << std::is_lvalue_reference<FTestB & amp; & amp;>::value << endl;//Output result: 0
cout << std::is_lvalue_reference<A & amp; & amp;>::value << endl;//Output result: 0
cout << std::is_lvalue_reference<int & amp; & amp;>::value << endl;//Output result: 0
cout << std::is_lvalue_reference<float & amp; & amp;>::value << endl;//Output result: 0
cout << std::is_lvalue_reference<ETestEnum & amp; & amp;>::value << endl;//Output result: 0
cout << endl;

//is_rvalue_reference determines whether the given type is an rvalue, if so it returns 1, if not it returns 0
cout << std::is_rvalue_reference<FTestB>::value << endl;//Output result: 0
cout << std::is_rvalue_reference<A>::value << endl;//Output result: 0
cout << std::is_rvalue_reference<int>::value << endl;//Output result: 0
cout << std::is_rvalue_reference<float>::value << endl;//Output result: 0
cout << std::is_rvalue_reference<ETestEnum>::value << endl;//Output result: 0

cout << std::is_rvalue_reference<FTestB & amp;>::value << endl;//Output result: 0
cout << std::is_rvalue_reference<A & amp;>::value << endl;//Output result: 0
cout << std::is_rvalue_reference<int & amp;>::value << endl;//Output result: 0
cout << std::is_rvalue_reference<float & amp;>::value << endl;//Output result: 0
cout << std::is_rvalue_reference<ETestEnum & amp;>::value << endl;//Output result: 0

cout << std::is_rvalue_reference<FTestB & amp; & amp;>::value << endl;//Output result: 1
cout << std::is_rvalue_reference<A & amp; & amp;>::value << endl;//Output result: 1
cout << std::is_rvalue_reference<int & amp; & amp;>::value << endl;//Output result: 1
cout << std::is_rvalue_reference<float & amp; & amp;>::value << endl;//Output result: 1
cout << std::is_rvalue_reference<ETestEnum & amp; & amp;>::value << endl;//Output result: 1
cout << endl;

//is_function determines whether the given type is a function, if so it returns 1, if not it returns 0
cout << std::is_function<FTestB>::value << endl;//Output result: 0
cout << std::is_function<A>::value << endl;//Output result: 0
cout << std::is_function<int>::value << endl;//Output result: 0
cout << std::is_function<float>::value << endl;//Output result: 0
cout << std::is_function<ETestEnum>::value << endl;//Output result: 0
cout << std::is_function<void(int a)>::value << endl;//Output result: 1
cout << std::is_function<void(int,int)>::value << endl;//Output result: 1
cout << std::is_function<void(...)>::value << endl;//Output result: 1
cout << endl;

//is_member_function_pointer determines whether the given type is a member function, if so it returns 1, if not it returns 0
cout << std::is_member_function_pointer<FTestB>::value << endl;//Output result: 0
cout << std::is_member_function_pointer<A>::value << endl;//Output result: 0
cout << std::is_member_function_pointer<int>::value << endl;//Output result: 0
cout << std::is_member_function_pointer<float>::value << endl;//Output result: 0
cout << std::is_member_function_pointer<ETestEnum>::value << endl;//Output result: 0
cout << std::is_member_function_pointer<void(int a)>::value << endl;//Output result: 0
cout << std::is_member_function_pointer<void(int, int)>::value << endl;//Output result: 0
cout << std::is_member_function_pointer<void(...)>::value << endl;//Output result: 0
cout << std::is_member_function_pointer<float(A::C::*)>::value << endl;//Output result: 0//Member object pointer
cout << std::is_member_function_pointer<double(A::C::*)()>::value << endl;//Output result: 1//Member object function
cout << endl;
\t
//is_array determines whether the given type is a container, if so it returns 1, if not it returns 0
cout << std::is_array<FTestB>::value << endl;//Output result: 0
cout << std::is_array<A>::value << endl;//Output result: 0
cout << std::is_array<int>::value << endl;//output result: 0
cout << std::is_array<float>::value << endl;//output result: 0
cout << std::is_array<ETestEnum>::value << endl;//output result: 0
cout << std::is_array<void(int a)>::value << endl;//output result: 0
cout << std::is_array<void(int, int)>::value << endl;//output result: 0
cout << std::is_array<void(...)>::value << endl;//output result: 0
cout << std::is_array<float(A::C::*)>::value << endl;//output result: 0
cout << std::is_array<double(A::C::*)()>::value << endl;//output result: 0
cout << std::is_array<vector<int>>::value << endl;//output result: 0
cout << std::is_array<int[]>::value << endl;//output result: 1
cout << endl;

//is_arithmetic determines whether the given type is an integer or a floating point type, if it returns 1, if not returns 0
cout << std::is_arithmetic<FTestB>::value << endl;//output result: 0
cout << std::is_arithmetic<A>::value << endl;//output result: 0
cout << std::is_arithmetic<int>::value << endl;//Output result: 1
cout << std::is_arithmetic<float>::value << endl;//output result: 1
cout << std::is_arithmetic<ETestEnum>::value << endl;//output result: 0
cout << std::is_arithmetic<void(int a)>::value << endl;//output result: 0
cout << std::is_arithmetic<void(int, int)>::value << endl;//Output result: 0
cout << std::is_arithmetic<void(...)>::value << endl;//Output result: 0
cout << std::is_arithmetic<float(A::C::*)>::value << endl;//Output result: 0
cout << std::is_arithmetic<double(A::C::*)()>::value << endl;//Output result: 0
cout << std::is_arithmetic<vector<int>>::value << endl;//Output result: 0
cout << std::is_arithmetic<int[]>::value << endl;//Output result: 0
cout << endl;

//is_abstract determines whether the given type is an abstract class, if so it returns 1, if not it returns 0
cout << std::is_abstract<ETestEnum>::value << endl;//Output result: 0
cout << std::is_abstract<FTestB>::value << endl;//Output result: 0
cout << std::is_abstract<A>::value << endl;//Output result: 0
cout << std::is_abstract<int>::value << endl;//Output result: 0
cout << std::is_abstract<float>::value << endl;//Output result: 0
cout << std::is_abstract<void(int, int)>::value << endl;//Output result: 0
cout << std::is_abstract<void(...)>::value << endl;//Output result: 0
cout << std::is_abstract<float(A::C::*)>::value << endl;//Output result: 0
cout << std::is_abstract<double(A::C::*)()>::value << endl;//output result: 0
cout << std::is_abstract<vector<int>>::value << endl;//output result: 0
cout << std::is_abstract<int[]>::value << endl;//output result: 0
cout << std::is_abstract<FInterface>::value << endl;//output result: 1
cout << endl;

return 0;
}

is_same

Determine whether the types are the same or not

learn.cpp

#include <iostream>
using namespace std;
#include <type_traits>

int main()
{<!-- -->
cout << std::is_same<int, int>::value << endl;//output result: 1

cout << std::is_same<int & amp;, int>::value << endl;//output result: 0

cout << std::is_same<int & amp;, std::add_lvalue_reference<int>::type>::value << endl;//output result: 1
//Added a reference to type
//add_lvalue_reference adds a reference, which can be seen in the type_traits library

cout << std::is_same<int, std::remove_const<const int>::type>::value << endl;//output result: 1
\t
cout << std::is_same<int & amp; & amp;, std::add_rvalue_reference<int>::type>::value << endl;//output result: 1

cout << std::is_same<const int, std::add_const<int>::type>::value << endl;//output result: 1

return 0;
}

Determine instance type through decltype

 int a = 10;
double b = 20;
cout << std::is_same<decltype(a), decltype(b)>::value << endl;

conslational

For conditional selection, different types are selected according to the result of the conditional expression

source code

If the first position bool _Test is true go to the second position class _Ty1, if the first position is not bool _Test go to the third position class _Ty2

template <bool _Test, class _Ty1, class _Ty2>
struct conditional {<!-- --> // Choose _Ty1 if _Test is true, and _Ty2 otherwise
    using type = _Ty1;
};

decltype

Used to infer the type of an expression.
get type

decltype is an operator introduced in C++11, used to obtain the type of expression, including variables, functions, expressions, etc.

The decltype operator determines the type of a given expression at compile time and returns a value of the same type as the expression without performing the actual expression or calculation. It can be used in two ways:

  1. Usage 1: decltype(expression), returns the type of expression.
  2. Usage 2: decltype((variable)), returns the reference type of the variable.

Example:

#include <iostream>

int main() {<!-- -->
    int x = 5;
    decltype(x) y = 10; // The type of y is int
    decltype((x)) z = y; // z is of type int & amp; (reference type)

    std::cout << "y: " << y << std::endl;
    std::cout << "z: " << z << std::endl;

    y = 20; // Modify the value of y
    std::cout << "x: " << x << std::endl; // Output 5, the value of x has not changed
    std::cout << "z: " << z << std::endl; // Output 20, z is a reference to x

    return 0;
}

decltype is very useful in metaprogramming and generic programming, and can perform type deduction based on the type of a variable or expression for further programming operations.

conslitional

learn.cpp

#include <iostream>
using namespace std;
#include <type_traits>

int main()
{<!-- -->
//If the first position is true, go to the second position int, if the first position is not true, go to the third position double
//If the first position is true, the following is int type, if the first position is not true, the following is double type
std::conditional<true, int, double>::type Hel1 = 100;
std::conditional<true, int, double>::type Hel2 = 100;

std::conditional<(sizeof(int)>sizeof(long double)), int, float>::type A = 100;

cout << typeid(A).name() << endl;//Output result: float

return 0;
}

decay

Remove operation

Used to get the “decay” type of a given type, stripping references, cv-qualifiers and converting array types to pointer types

#include <iostream>
using namespace std;
#include <type_traits>

int main()
{<!-- -->
std::decay<int & amp;>::type A1;//int type, left reference removed
std::decay<int & amp; & amp;>::type A2;//int type, remove the right reference
std::decay<const int>::type A3;//int type, remove const
std::decay<int[100]>::type A4;//int*, convert the array into a pointer
std::decay<int(int)>::type A5;//int(*)(int) function pointer type, indicating a pointer to a function that points to an int type parameter and returns an int type value

return 0;
}

CV Qualifier

CV qualifiers are modifiers used in C++ to qualify types for constness and variability. CV stands for the abbreviation of the keywords “const” and “volatile”.

  • The const keyword is used to declare a constant, indicating that the value of a variable cannot be changed after its initialization. That is, a variable modified by const can only read its value and cannot modify it. For example: const int num = 10;
  • The volatile keyword is used to declare volatility, indicating that the value of a variable may be modified without notice. volatile is usually used for variables with special requirements, such as hardware registers. Read and write operations on volatile variables will be considered visible by the compiler and will not be optimized. For example: volatile int flag = 0;

CV qualifiers can be used alone or together. For example, const volatile int data = 100; indicates that data is both a read-only constant and volatile. Such variables are common in multi-threaded or embedded system development to enable safe access to shared resources.

enable_if

Determine whether it is an integer or floating point type. Integer type returns true, floating point type (float, double) returns false. Other types cannot be added, and an error will be reported.
But it can solve the problem of not being able to add other types, the following code, add a new template, add a !, and use the new template when it is judged that it is not an integer or floating point type

Used to select whether to enable a specific function template based on a certain condition in the template

#include <iostream>
using namespace std;
#include <type_traits>

template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type Func(T f)
{<!-- -->
return f;
}

class A
{<!-- -->
public:
A() {<!-- -->}
};

//Modified version, you can put custom classes (added a!, so that it does not conform to the above template)
template<class T>
typename std::enable_if<!std::is_arithmetic<T>::value, T>::type Func(T f)
{<!-- -->
cout << "F" << endl;
return f;
}

int main()
{<!-- -->
A a;
auto a1 = Func(1);//Return a type
auto a2 = Func(1.1f);//The normal version can only store integers and floating point types. The following is the normal version that will report an error when placing a custom class.
//auto a3 = Func(a);//will report an error

auto a4 = Func(a);

return 0;
}

integral_constant

Define precompiler constants
Used to represent the type of compile-time constants, which can be used for compile-time conditional judgment.

#include <iostream>
using namespace std;
#include <type_traits>

int main()
{<!-- -->
//Define precompiler constants, no longer need enumerations, use typedef
typedef std::integral_constant<int, 100>::type Hello;
Hello o;//Its type is Hello defined above, so the value of o is 100
int i1 = o;//Implicit type conversion occurs
int i2 = static_cast<int>(o);//Convert object o to integer type and assign the conversion result to integer variable i2
int i3 = o.operator Hello::value_type();

cout << i1 << endl;
cout << i2 << endl;
cout << i3 << endl;

return 0;
}

Find the largest integer

class.h

#pragma once

template<int a,int...Args>
struct IntMax;

template<int a>
struct IntMax<a>:std::integral_constant<int,a>
{<!-- -->

};

template<int a1,int a2, int...Args>
struct IntMax<a1, a2, Args...> :std::integral_constant<int, a1 >= a2 ? IntMax<a1, Args...>::value : IntMax<a2, Args...>::value >
{<!-- -->

};

learn.cpp

#include <iostream>
using namespace std;
#include <type_traits>
#include "class.h"

int main()
{<!-- -->
// faster than any algorithm
cout << IntMax<1, 2, 300, 4, 5, 6, 7, 8, 9, 100>::value << endl;//Output result: 300
return 0;
}

Seeking memory alignment

Memory alignment can be understood as finding a suitable location for data in computer memory so that the processor can efficiently read or write these data.

Imagine putting data in an odd location in memory, maybe not stored in the right way, and the processor might need multiple operations to read or write that data. This wastes time and resources and can cause the program to slow down.

Therefore, memory alignment is to adjust the storage location of data so that the starting address of the data meets certain rules. This rule is usually an integer multiple of the size of the data type. This way, the processor can read or write the entire data directly from memory without additional operations.

Through reasonable memory alignment, the operating efficiency of the program can be improved, allowing the program to process data more quickly. It’s like when workers put the tools neatly in the right place when they are working, they can use the tools more conveniently and improve work efficiency.

For example, compare int, double, float, long double, etc., because of different types, variables of different types are placed in memory according to reasonable rules

class.h

#pragma once

template<int a,int...Args>
struct IntMax;

template<int a>
struct IntMax<a>:std::integral_constant<int,a>
{<!-- -->

};

template<int a1,int a2, int...Args>
struct IntMax<a1, a2, Args...> :std::integral_constant<int, a1 >= a2 ? IntMax<a1, Args...>::value : IntMax<a2, Args...>::value >
{<!-- -->

};

template
struct MaxAlign : std::integral_constant::value...>::value>
{

};

learn.cpp

#include <iostream>
using namespace std;
#include <type_traits>
#include "class.h"

int main()
{<!-- -->
//Faster than any algorithm
cout << IntMax<1, 2, 300, 4, 5, 6, 7, 8, 9, 100>::value << endl;//output result: 300
\t
struct FA
{<!-- -->
int a;
int b;
double a1;
float a2;
long double a3;
};

int A = alignof(FA);
cout << A << endl;//Output result: 8 (so the above alignment is based on 8)

cout << MaxAlign<char, int, float, long, long long>::value << endl;//output result: 8//according to the largest alignment of memory
return 0;
}

alignment_of

Get the alignment of the specified type