Type traits#
Klasy cech (type traits) to technika programowania w C++ pozwalająca na:
modyfikację typów na etapie kompilacji (np. usunięcie lub dodanie do typu modyfikatora const)
uzyskanie informacji o typach w czasie kompilacji (np. sprawdzenie czy typ jest wskaźnikiem)
Meta-funkcje#
Technika cechowania wykorzystuje meta-funkcje, najczęściej implementowane jako szablony klas przyjmujące jako parametr cechowany typ i zwracające:
type_trait<T>::type- nowy typ będący efektem transformacji (np. usunięcia modyfikatoraconst)type_trait<T>::value- stałą statyczną wartośćconstexpr(najczęściej typubool)
Meta-funkcje zwracające wartość#
Przykład implementacji meta-funkcji zwracającej stałą wartość ewaluowaną na etapie kompilacji:
template<typename T>
struct TypeSize
{
static constexpr std::size_t value = sizeof(T);
};
Dla meta-funkcji zwracającej wartość można zdefiniować pomocniczy szablon zmiennej o nazwie kończącej się na _v:
template<typename T>
constexpr std::size_t TypeSize_v = TypeSize<T>::value;
Wykorzystanie meta-funkcji:
std::array<std::byte, TypeSize_v<double>> buffer;
Meta-funkcje zwracające typy#
Najprostszym przykładem meta-funkcji zwracającej typ jest klasa ‘Identity’:
template<typename T>
struct Identity
{
using type = T;
};
Pomocniczy alias szablonu:
template<typename T>
using Identity_t = typename Identity<T>::type;
Przy implementacji meta-funkcji często stosujemy specjalizację szablonów:
template <typename T1, typename T2>
struct IsSame
{
static constexpr bool value = false;
};
template <typename T>
struct IsSame<T, T>
{
static constexpr bool value = true;
};
template <typename T1, typename T2>
using IsSame_v = IsSame<T1, T2>::value;
Meta-funkcje mogą być wykorzystane statycznych asercjach:
static_assert(IsSame_v<int, Identity_t<int>>);
Stałe całkowite - integral constants#
Szablon std::integral_constant pozwala opakować w postaci
meta-funkcji stałą zdefiniowaną na etapie kompilacji.
template<class T, T v>
struct integral_constant
{
static constexpr T value = v;
typedef T value_type;
typedef integral_constant type; // using injected-class-name
constexpr operator value_type() const noexcept { return value; }
constexpr value_type operator()() const noexcept { return value; } //since c++14
};
Wykorzystanie meta-funkcji integral_constant wygląda następująco:
static_assert(integral_constant<int, 5>::value == 5);
Biblioteka standardowa definiuje pomocniczy alias dla stałej typu bool:
template <bool v>
using bool_constant = integral_constant<bool, v>;
Zdefiniowane są również dwa typowe przypadki takich stałych:
using true_type = bool_constant<true>; // integral_constant<bool, true>
using false_type = bool_constant<false>; // integral_constant<bool, false>
Klasy cech typów#
Klasy cech transformujące typy#
Często w trakcie pisania kodu szablonowego zachodzi potrzeba transformacji typu określonego
parametru szablonu (np. wymagane jest usunięcie lub dodanie referencji, modyfikatora const lub volatile, itp.).
W takim przypadku możemy posłużyć się klasą cechy transformującej (implementowaną jako meta-funkcja):
template <typename T>
struct remove_reference
{
using type = T;
};
template <typename T>
struct remove_reference<T&>
{
using type = T;
};
template <typename T>
struct remove_reference<T&&>
{
using type = T;
};
Od C++14 do klas cech dodane są odpowiednie aliasy szablonów, które umożliwiają uniknięcie konieczności deklaracji typename
przed zagnieżdżonym typem type:
template <typename T>
using remove_reference_t = typename remove_reference<T>::type;
Użycie cech transformujących wygląda następująco:
template <typename T>
constexpr std::remove_reference_t<T>&& move(T&& t) noexcept
{
return static_cast<std::remove_reference_t<T>&&>(t);
}
Cechy transformujące typy w bibliotece standardowej#
Biblioteka standardowa w nagłówku <type_traits> definiuje zbiór klas cech transformujących:
Cecha |
Rezultat |
|---|---|
|
usuwa referencję z typu ( |
|
dodaje lvalue referencję ( |
|
dodaje rvalue referencję ( |
|
usuwa wskaźnik z typu ( |
|
dodaje wskaźnik ( |
|
usuwa modyfikator |
|
usuwa modyfikator |
|
usuwa modyfikatory |
|
dodaje modyfikator |
|
dodaje modyfikator |
Cecha std::decay#
Przydatną cechą jest zdefiniowana w bibliotece standardowej cecha std::decay.
Dokonuje ona transformacji odpowiadającej następującym przekształceniom:
usuwane są referencje
usuwane są modyfikatory
constlubvolatiletablice konwertowane są do wskaźników
funkcje konwertowane są do wskaźników do funkcji
template <typename T, typename U>
void check_decay()
{
static_assert(std::is_same_v<std::decay_t<T>, U>);
}
check_decay<int&, int>();
check_decay<const int&, int>();
check_decay<int&&, int>();
check_decay<int(int), int(*)(int)>();
check_decay<int[20], int*>();
check_decay<const int[20], const int*>();
Klasy cech - predykaty#
Implementacja cech typów, które pełnią rolę predykatów, polega zwykle na zdefiniowaniu ogólnego
szablonu dziedziczącego po false_type (dla typów nie posiadających
określonej cechy). Kolejnym krokiem jest dostarczenie wersji
specjalizowanej szablonu dla typów z cechą, która dziedziczy po typie
true_type.
Specjalizacja szablonu może być całkowita:
template <typename T>
struct is_void : std::false_type
{};
template <>
struct is_void<void> : std::true_type
{};
// helper variable template - since C++17
template <typename T>
constexpr bool is_void_v = is_void<T>::value;
using T = void;
static_assert(is_void_v<T>, "T must be void");
using U = int;
static_assert(!is_void_v<U>, "U must not be void");
lub częściowa:
template <typename T>
struct is_pointer : std::false_type{};
template <typename T>
struct is_pointer<T*> : std::true_type{};
template <typename T>
constexpr bool is_pointer_v = is_pointer<T>::value;
Klasa cechy zwracającej wartość typu bool może być wykorzystana w statycznych asercjach:
template <typename T>
void max_value(T a, T b)
{
static_assert(is_pointer_v<T>, "T must be a pointer");
assert(a != nullptr);
assert(b != nullptr);
return (*a < *b) ? b : a;
}
Standardowe cechy typów - predykaty#
Biblioteka standardowa definiuje szeroki zbiór meta-funkcji, które umożliwiają odpytanie na etapie kompilacji, czy dany typ posiada odpowiednie cechy.
Cechy podstawowe#
Cecha podstawowa |
Rezultat |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Cechy kompozytowe#
Cechy kompozytowe są kompozycją najczęściej kilku cech podstawowych.
Cecha grupowana |
Rezultat |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Właściwości typów#
Standard używa terminu właściwość typu w celu zdefiniowania cechy opisującej wybrane atrybuty typu.
Wybrane właściwości typu:
Właściwość typu |
Rezultat |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|