trainings/AdvancedCppV2/Presentation/templates_specjalization.md

3.6 KiB

Specialization


Function specialization

If we want to have the same function name, but we want our code to behave differently for some types, we can create a specialization.

//generic function
template <typename T>
void print(T arg) {
    std::cout << arg << '\n';
}
// specialization for `T = double`
template <>
void print<double>(double arg) {
    std::cout << std::setprecision(10) << arg << '\n';
}
// better: overload
void print(double arg) {
    std::cout << std::setprecision(10) << arg << '\n';
}

Tip: do not use function specializations. Always prefer function overloads.

Template function specializations do not take part in overload resolution. Only the exact type match is considered.

Above specialization does not work for float. Overload does.


Class specialization

A class can have not only different behaviour (different methods implementations) but also different layouts. You can have completely different fields and/or their values.


Specialization example #1 - methods

#include <iostream>

template<typename T>   // primary template
struct is_int {
    bool get() const { return false; }
};

template<>  // explicit specialization for T = int
struct is_int<int> {
    bool get() const { return true; }
};


int main() {
    is_int<char> iic;
    is_int<int> iii;
    std::cout << iic.get() << '\n';  // prints 0 (false)
    std::cout << iii.get() << '\n';  // prints 1 (true)
    return 0;
}

Specialization example #2 - field values

#include <iostream>

template<typename T>   // primary template
struct is_int {
    static constexpr bool value = false;
};

template<>  // explicit specialization for T = int
struct is_int<int> {
    static constexpr bool value = true;
};


int main() {
    std::cout << is_int<char>::value << '\n';  // prints 0 (false)
    std::cout << is_int<int>::value << '\n';   // prints 1 (true)
    return 0;
}

You can play with the code here


Specialization example #3 - <type_traits>

To achieve the last behavior, we can use std::false_type and std::true_type. The below code is equivalent to the one from the previous example.

#include <iostream>
using namespace std;

template<typename T>   // primary template
struct is_int : std::false_type
{};

template<>  // explicit specialization for T = int
struct is_int<int> : std::true_type
{};

int main() {
    std::cout << is_int<char>::value << std::endl;  // prints 0 (false)
    std::cout << is_int<int>::value << std::endl;   // prints 1 (true)
    return 0;
}

The interactive version of this code is here


Exercise - is_int_key

In VectorMap write a class constant is_int_key that holds a boolean value. It should be true when the key is int and false otherwise.

Generally, it should do the same job as the isIntKey() method, but we want to have it available even without having an object.

Take a look in the <type_traits> library for that. It should be useful 🙂