# What is C++ Metafunction and How to Use It?

In C++ metaprogramming, a metafunction receives types and/or integral values, and after performing some logics returns types and/or integral values. Normal functions manipulate values, but the focus of a metafunction is types.

## Definition

A metafunction is defined via `struct`

. This is a simple metafunction which returns the input type and value:

```
template<typename T, int i> // T is input
struct GetMyInfo{
using type = T; // type is output type
static constexpr int value = i; // value is output value
};
```

The content of `<>`

is the metafunction parameters. `type`

and `value`

are the output.

Let’s employ the metafunction:

```
GetMyInfo<double, 2>::type x; // double x;
```

We defined a variable x with the output type of the metafunction which is `double`

.

The output value can be used also:

```
std::cout<<GetMyInfo<double, 2>::value; // prints 2
```

## Condition via template specialization

A condition is achieved via template specialization. If multiple struct templates accept some parameters, a compiler chooses the one that is more specific. Let’s define this pseudo-function:

```
// pseudo-code
f(bool cond){
if cond==true return int
if cond==false return double
}
```

The code is like this

```
template<bool cond>
struct GetType{
using type = int;
};
template<>
struct GetType<false>{
using type = double;
};
GetType<true>::type i; // int i;
GetType<false>::type d; // double d;
```

- The first case is the default or generic case as it accepts both
`false`

and`true`

. - The second case is specific, only accepts
`false`

. - Therefore, if we pass
`true`

, only the first case is matched and selected. - But passing
`false`

both cases are matched but the second case is selected because it is more specific.

Let’s write a metafunction that returns true if a type is double:

```
template<typename T>
struct IsDouble{
static constexpr bool value = false;
};
template<>
struct IsDouble<double>{
static constexpr bool value = true;
};
int main(){
std::cout<<IsDouble<double>::value<<"\n"; // true
std::cout<<IsDouble<int>::value<<"\n"; // false
return 0;}
```

The same ideas as previous expample:

- The first struct is the default case which accepts any type.
- The second struct is the specific case which only accepts double. This will be selected for
`double`

because it is more specific.

## True and false types

To avoid excessive writing, there are `std::true_type`

and `std::false_type`

that a struct can inherit from. Let’s write `IsDouble<T>`

again:

```
template<typename T>
struct IsDouble : std::false_type{};
template<>
struct IsDouble<double>:std::true_type{};
int main(){
std::cout<<IsDouble<double>::value<<"\n"; // true
}
```

## Same types

Let’s write a metfunction that return true if two types are the same:

```
template<typename T, typename U>
struct AreSame: std::false_type{};
template<typename T>
struct AreSame<T,T>: std::true_type{};
int main(){
std::cout<<AreSame<int,int>::value<<"\n"; //true
std::cout<<AreSame<int,double>::value<<"\n"; // false
return 0;
}
```

This metafunction is similar to `std::is_same<T,U>`

in `type_traits`

header. There are many useful traits in that header, check them before writing your metafunctions.

## Is pointer

A metafunction that tells us if a type is a pointer and also returns the type of pointer’s target would be like:

```
template<typename T>
struct IsPointer{
static constexpr bool value = false;
using innerType = T;
};
template<typename T>
struct IsPointer<T*>{
static constexpr bool value = true;
using innerType = T;
};
int main(){
std::cout<<IsPointer<int*>::value; // true
std::cout<<IsPointer<int>::value; // false
IsPointer<int*>::innerType x; // int x;
IsPointer<int>::innerType y; // int y;
return 0;
}
```

This is similar to `std::is_pointer<T>`

from `type_traits`

header.

Can you write a metafunction that tells if a type is reference?

## More conditions

To this point, we focused on true/false cases, but template specialization can be expanded for many conditions. For example, let’s write a metafunction that has this pseudo-code

```
f(int i)
if i==0 return bool
if i==1 return int
else return double
```

The metafunction is:

```
template<int i>
struct SelectType { using type = double;};
template<>
struct SelectType<0> { using type = bool;};
template<>
struct SelectType<1> { using type = int;};
SelectType<0>::type y; // bool y;
SelectType<1>::type z; // int z;
SelectType<10>::type x; // double x;
```

## Extract inner-types

Using metafunctions, for a type `U<T>`

, we can extract sub-type T. For example, let’s write a meta-function that extracts the inner type of `std::vector<T>`

, T.

```
template<typename T>
struct GetVectorSubType{};
template<typename T>
struct GetVectorSubType<std::vector<T>>{
using type = T;
};
GetVectorSubType<std::vector<int>>::type x; // int x;
GetVectorSubType<double>::type x; // Error...
```

Note that because the default case has no `type`

definition, we get an error if any type other than `std::vector<T>`

is passed.

Now let’s write another one that returns the size and inner type of `std::array`

:

```
template<typename T>
struct GetArrayInfo{
static constexpr bool isArray = false;
};
template<typename T, size_t n>
struct GetArrayInfo<std::array<T,n>>{
using type = T;
static constexpr size_t size = n;
static constexpr bool isArray = true;
};
int main(){
int i;
std::array<double,3> arr;
std::cout<<GetArrayInfo<decltype(i)>::isArray; // false
std::cout<<GetArrayInfo<decltype(arr)>::isArray; // true
std::cout<<GetArrayInfo<decltype(arr)>::size; //3
using ArrayType = GetArrayInfo<decltype(arr)>::type;
std::cout<<std::is_same_v<ArrayType, double>; // true
return 0;
}
```

## Integral constant

Integer values are accepted as template parameters but for a metafunction that accepts only types, like `std::is_same<T,U>`

the integer values cannot be used. To overcome that we can define a type for integer values:

```
template<int i>
struct int_const{
static constexpr int value = i;
};
using one_t = int_const<1>;
using two_t = int_const<2>;
using namespace std;
int main(){
cout<<is_same_v<one_t,two_t>; // false
cout<<is_same_v<one_t,one_t>; // true
return 0;
}
```

This is very similar to `std::integral_const`

. This type template is useful in tag-dispatch technique.

## Constexpr for value calculation

Before constexpr of C++11, metafunctions were used for compile-time calculations. For example, Factorial function is found this way:

```
template<int i>
struct Factorial{
static const int value = i * Factorial<i-1>::value;
};
template<>
struct Factorial<0>{
static const int value = 1;
};
```

Since C++11, we have `constexpr`

functions which are possible to be calculated at compile-time, see the factorial using `constexpr`

:

```
constexpr int factorial(int n)
{
return n <= 1 ? 1 : (n * factorial(n - 1));
}
```

This is way cleaner than using templates, therefore, in the new codes, where possible, use constexpr functions instead of templates for value calculations.

## Constexpr for higher-level logic

Having some basic metafunctions (or type traits), we can use `if-constexpr`

and `constexpr`

functions instead of writing more metafunctions for complex logic.

For example let’s write a function that receives an object, and returns the size of it. I am using `GetArrayInfo`

metafunction I defined previously:

```
template<typename T>
auto printSize(T t){
if constexpr(GetArrayInfo<T>::isArray)
std::cout<< t.size();
else if constexpr(std::is_same_v<T,std::string>)
std::cout<< 1;
else
std::cout<<"Unknown type";
}
using namespace std;
int main(){
int i;
array<int,3> arr;
printSize(i); // unknown type
printSize(arr); // 3
return 0;
}
```

Another example, let’s write a function that

```
if 0≤i<3
returns int
if 3≤i<5
returns float
if 3≤i<7
returns double
else
shows error
```

The code will be like this:

```
template<int i>
auto f(){
if constexpr (0<=i && i<3)
return int{};
else if constexpr (3<=i && i<5)
return float{};
else
return double{};
}
using namespace std;
int main(){
using outcome1_t = decltype(f<1>());
using outcome4_t = decltype(f<4>());
using outcome7_t = decltype(f<7>());
cout<<boolalpha<<is_same_v<outcome1_t, int>;
cout<<boolalpha<<is_same_v<outcome4_t, float>;
cout<<boolalpha<<is_same_v<outcome7_t, double>;
return 0;
}
```

## Lambda

Also, it’s good to know that since C++20, a lambda can be used in metafunctions to set values and types:

```
template<typename T>
struct Get{
static constexpr int byteSize = 0;
};
template<>
struct Get<int>{
static constexpr int byteSize = [](){return 4;}();
using type = decltype( [](){return int{};}() );
};
```