std::function
Пусть void(int,int)
- это тип функции, принимающей два инта и возвращающей ничего, тогда мы можем похранить ее в std::function
следующим образом.
void print(int a, int b) {
std::cout << a << " " << b << endl;
}
std::function<void(int, int)> func = print;
func(1, 2); // output: 1 2
Похранить функции с другой сигнатурой мы тоже, конечно, можем.
С помощью function
мы так же можем хранить и лямбды (и любой другой функциональный объект).
void f(bool flag) {
std::function<void(int,int)> func;
if (flag) {
func = [](int a, int b){};
} else {
// note: все лямбды имеют разный тип
func = [](int a, int b){};
}
}
Примечательно, что классу function
достаточно знать только тип функции и только на этапе объявления объекта.
Этот класс реализует паттерн type erasure
. Тот же самый паттерн встречается и в других классах STL.
std::optional
Класс, который хранит опциональное значение (либо шаблонный тип, либо std::nullopt_t
).
std::optional<int> convert(std::function<void(int)> &f) {
// ....
if (!fail) {
return result;
}
return {};
}
int main() {
auto val = convert(...);
if (val.has_value()) {
std::cout << "OK";
} else {
std::cout << "Fail";
}
}
std::any
Тип any
хранит в себе объект любого типа. Так одна и та же переменная типа any
может сначала хранить int
, затем float
, а затем строку.
Требуется каст для обратного преобразования.
std::any a = 42;
int v = std::any_cast<int&>(a);
a = std::string("hello");
std::string s = std::any_cast<std::string&>(a);
// a = mytype(); и так далее
Если в качестве шаблонного параметра any_cast
был передан любой тип, отличный от типа текущего хранимого объекта, будет выброшено исключение bad_any_cast
.
Если экземпляр any
разрушается деструктором, то он корректно удаляет хранимый объект.
std::variant
Шаблонный класс, который представляет собой типобезопасный union
, который помнит, какой тип он хранит. В отличие от union
, variant
позволяет хранить не только POD-типы (тривиальные типы или тривиальные классы).
std::variant<int, float, char> v;
v = 3.14f;
v = 42;
std::cout << std::get<int>(v);
Для получения значений из variant
используется функция get
. Она выбросит исключение bad_variant_access
, если попытаться взять не тот тип.
Говоря про доступ к варианту, нельзя не упомянуть visit
, принимающий функцию, которая должна уметь принимать любой тип из данного variant
.
std::variant<int, float, char> v;
v = 42;
std::visit([](auto& arg) {
using Type = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<Type, int>) {
std::cout << "int: " << arg;
} else if constexpr (std::is_same_v<Type, float>) {
std::cout << "float: " << arg;
} else if constexpr (std::is_same_v<Type, char>) {
std::cout << "char: " << arg;
}
}, v);
Заимствования:
cpp-notes/19_lambdas_type_erasure.md at master · lejabque/cpp-notes (github.com)