std::variant<Types...>::variant
Материал из cppreference.com
<tbody>
</tbody>
constexpr variant() noexcept(/* смотрите ниже */); |
(1) | (начиная с C++17) |
constexpr variant( const variant& other ); |
(2) | (начиная с C++17) |
constexpr variant( variant&& other ) noexcept(/* смотрите ниже */); |
(3) | (начиная с C++17) |
template< class T > constexpr variant( T&& t ) noexcept(/* смотрите ниже */); |
(4) | (начиная с C++17) |
template< class T, class... Args > constexpr explicit variant( std::in_place_type_t<T>, Args&&... args ); |
(5) | (начиная с C++17) |
template< class T, class U, class... Args > constexpr explicit variant( std::in_place_type_t<T>, std::initializer_list<U> il, Args&&... args ); |
(6) | (начиная с C++17) |
template< std::size_t I, class... Args > constexpr explicit variant( std::in_place_index_t<I>, Args&&... args ); |
(7) | (начиная с C++17) |
template< std::size_t I, class U, class... Args > constexpr explicit variant( std::in_place_index_t<I>, std::initializer_list<U> il, Args&&... args ); |
(8) | (начиная с C++17) |
Создаёт новый объект variant.
1) Конструктор по умолчанию. Создаёт
variant, содержащий инициализированное значением значение первой альтернативы (index() равно нулю).
- Этот конструктор является
constexprтогда и только тогда, когда инициализация значения альтернативного типаT_0соответствует требованиям для функций constexpr. - Эта перегрузка участвует в разрешении перегрузки, только если
std::is_default_constructible_v<T_0>равноtrue.
2) Конструктор копирования. Если
other не является valueless_by_exception, создаёт variant, содержащий ту же альтернативу, что и other, и напрямую инициализирует содержащееся значение с помощью *std::get_if<other.index()>(std::addressof(other)). Иначе инициализирует вариант valueless_by_exception.
- Этот конструктор определяется как удалённый, если только
std::is_copy_constructible_v<T_i>не равноtrueдля всехT_iвTypes.... - Он тривиальный, если
std::is_trivially_copy_constructible_v<T_i>равноtrueдля всехT_iвTypes....
3) Конструктор перемещения. Если
other не является valueless_by_exception, создаёт variant, содержащий ту же альтернативу, что и other, и напрямую инициализирует содержащееся значение с помощью std::move(*std::get_if<other.index()>(std::addressof(other))). Иначе инициализирует вариант valueless_by_exception.
- Эта перегрузка участвует в разрешении перегрузки, только если
std::is_move_constructible_v<T_i>равноtrueдля всехT_iвTypes.... - Он тривиальный, если
std::is_trivially_move_constructible_v<T_i>равноtrueдля всехT_iвTypes....
4) Преобразующий конструктор. Создаёт
variant, содержащий альтернативный тип T_j, который будет выбран разрешением перегрузки для выражения F(std::forward<T>(t)), если одновременно произошла перегрузка воображаемой функции F(T_i) для каждого T_i из Types... в области видимости, за исключением того, что:
- Перегрузка
F(T_i)рассматривается только в том случае, если объявлениеT_i x[] = { std::forward<T>(t) };допустимо для некоторой искусственной переменнойx.
- Перегрузка
Инициализирует напрямую содержащееся значение, как если бы это была прямая инициализация не списком из
std::forward<T>(t).
- Эта перегрузка участвует в разрешении перегрузки, только если
sizeof...(Types) > 0,std::decay_t<T>(до C++20)std::remove_cvref_t<T>(начиная с C++20) не является ни тем же типом, чтоvariant, ни специализацией std::in_place_type_t, ни специализацией std::in_place_index_t,std::is_constructible_v<T_j, T>равноtrue,- и выражение
F(std::forward<T>(t))(где F это вышеупомянутый набор мнимых функций) является корректным.
- Этот конструктор является конструктором
constexpr, если конструктор, выбранныйT_j, является конструктором constexpr.
std::variant<std::string> v("abc"); // OK
std::variant<std::string, std::string> w("abc"); // некорректно
std::variant<std::string, const char*> x("abc"); // OK, выбирает const char*
std::variant<std::string, bool> y("abc"); // OK, выбирает string;
// bool не является кандидатом
std::variant<float, long, double> z = 0; // OK, содержит long
// float и double не являются кандидатами
5) Создаёт
variant с указанной альтернативой T и инициализирует содержащееся значение аргументами std::forward<Args>(args)....
- Если выбранный конструктор
Tявляется конструкторомconstexpr, этот конструктор также является конструкторомconstexpr. - Эта перегрузка участвует в разрешении перегрузки, только если имеется ровно одно вхождение
TвTypes...иstd::is_constructible_v<T, Args...>равноtrue.
6) Создаёт
variant с указанной альтернативой T и инициализирует содержащееся значение аргументами il, std::forward<Args>(args)....
- Если выбранный конструктор
Tявляется конструкторомconstexpr, этот конструктор также является конструкторомconstexpr. - Эта перегрузка участвует в разрешении перегрузки, только если имеется ровно одно вхождение
TвTypes...иstd::is_constructible_v<T, initializer_list<U>&, Args...>равноtrue.
7) Создаёт
variant с альтернативой T_i, указанной индексом I, и инициализирует содержащееся значение аргументами std::forward<Args>(args)....
- Если выбранный конструктор
T_iявляется конструкторомconstexpr, этот конструктор также является конструкторомconstexpr. - Эта перегрузка участвует в разрешении перегрузки, только если
I < sizeof...(Types)иstd::is_constructible_v<T_i, Args...>равноtrue.
8) Создаёт
variant с альтернативой T_i, указанной индексом I, и инициализирует содержащееся значение аргументами il, std::forward<Args>(args)....
- Если выбранный конструктор
T_iявляется конструкторомconstexpr, этот конструктор также является конструкторомconstexpr. - Эта перегрузка участвует в разрешении перегрузки, только если
I < sizeof...(Types)иstd::is_constructible_v<T_i, std::initializer_list<U>&, Args...>равноtrue.
Параметры
| other | — | другой объект variant, содержащий значение для копирования/перемещения
|
| t | — | значение, которым инициализировать содержащееся значение |
| args... | — | аргументы, которыми инициализировать содержащееся значение |
| il | — | список инициализаторов, которыми инициализировать содержащееся значение |
Исключения
1) Может вызвать любое исключение, вызванное инициализацией значения первой альтернативы.
спецификация noexcept:
noexcept(std::is_nothrow_default_constructible_v<T_0>)2) Может вызвать любое исключение, вызванное прямой инициализацией любого
T_i в Types...3) Может вызвать любое исключение, вызванное созданием перемещением любого
T_i в Types.... спецификация noexcept:
noexcept((std::is_nothrow_move_constructible_v<Types> && ...))4) Может вызвать любое исключение, вызванное инициализацией выбранной альтернативы
T_j. спецификация noexcept:
noexcept(std::is_nothrow_constructible_v<T_j, T>)5-8) Может вызвать любое исключение, вызванное выбранным конструктором выбранной альтернативы.
Пример
Запустить этот код
#include <cassert>
#include <iostream>
#include <string>
#include <variant>
#include <vector>
using vector_t = std::vector<int>;
auto& operator<<(auto& out, const vector_t& v)
{
out << "{ ";
for (int e: v)
out << e << ' ';
return out << '}';
}
int main()
{
// инициализация значением первой альтернативы
std::variant<int, std::string> var0;
assert(std::holds_alternative<int>(var0) and
var0.index() == 0 and
std::get<int>(var0) == 0);
// инициализация первой альтернативы с помощью std::string{"STR"};
std::variant<std::string, int> var1{"STR"};
assert(var1.index() == 0);
std::cout << "1) " << std::get<std::string>(var1) << '\n';
// инициализация второй альтернативы с помощью int == 42;
std::variant<std::string, int> var2{42};
assert(std::holds_alternative<int>(var2));
std::cout << "2) " << std::get<int>(var2) << '\n';
// инициализация первой альтернативы с помощью std::string{4, 'A'};
std::variant<std::string, vector_t, float> var3
{
std::in_place_type<std::string>, 4, 'A'
};
assert(var3.index() == 0);
std::cout << "3) " << std::get<std::string>(var3) << '\n';
// инициализация второй альтернативы с помощью std::vector{1,2,3,4,5};
std::variant<std::string, vector_t, char> var4
{
std::in_place_type<vector_t>, {1,2,3,4,5}
};
assert(var4.index() == 1);
std::cout << "4) " << std::get<vector_t>(var4) << '\n';
// инициализация первой альтернативы с помощью std::string{"ABCDE", 3};
std::variant<std::string, vector_t, bool> var5 {std::in_place_index<0>, "ABCDE", 3};
assert(var5.index() == 0);
std::cout << "5) " << std::get<std::string>(var5) << '\n';
// инициализация второй альтернативы с помощью std::vector(4, 42);
std::variant<std::string, vector_t, char> var6 {std::in_place_index<1>, 4, 42};
assert(std::holds_alternative<vector_t>(var6));
std::cout << "6) " << std::get<vector_t>(var6) << '\n';
}
Вывод:
1) STR
2) 42
3) AAAA
4) { 1 2 3 4 5 }
5) ABC
6) { 42 42 42 42 }
Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
| Номер | Применён | Поведение в стандарте | Корректное поведение |
|---|---|---|---|
| LWG 2901 | C++17 | предусмотрены конструкторы, поддерживающие аллокаторы, но variant должным образом не поддерживает аллокаторы
|
конструкторы удалены |
| WG не указан | C++17 | конструктор преобразования шаблона плохо взаимодействует с выводом аргумента шаблонного класса |
добавлено ограничение |
| LWG 3024 | C++17 | конструктор копирования не участвует в разрешении перегрузки, если какой-либо тип элемента не является копируемым |
вместо этого определяется как удалённый |
| WG не указан | C++17 | конструкторы копирования/перемещения не могут быть тривиальными, даже если базовые конструкторы тривиальны |
требуется для распространения тривиальности |
| WG не указан | C++17 | конструктор преобразования вслепую собирает набор перегрузок, что приводит к непреднамеренным преобразованиям |
сужающие и логические преобразования не учитываются |
| WG не указан | C++17 | конструктор преобразования для bool не разрешал неявноепреобразование |
преобразование указателя в bool сужает, иконструктор преобразования не имеет исключений для bool
|