Article directory
- Preface and requirements
- union
-
- memory map
- C++11 union
- use
-
- ref example
- structure
-
- Ordinary structure
- Blanking
- emplace
- monostate
- access
-
- std::get<>
- std::holds_alternative<>
- Get pointer std::get_if<>
- Get the optional number std::variant_size
- END
Foreword and requirements
Union is a concept that has existed since the C language era. Mainly used in some scenarios with large memory limitations.
However, traditional C has too many restrictions, but C++ provides a safer type std::variant
union
Memory map
Let’s first look at a C language data structure memory mapping diagram.
! Internet picture (not original), invaded and deleted!
All fields in a union share a common memory area.
C++11 union
In old-time unions members cannot be of a special type. In C++11, the restriction has been lifted, as long as it is a non-application type.
In fact, union can also be discussed in a separate article. Here is a simple example without going into too much explanation:
#include <iostream> #include <string> #include <vector> union U {<!-- --> U() {<!-- --> } ~U() {<!-- --> } static int s; int x; float y; std::string str; std::vector<int> vec; }; // Static members of the same class are similar int U::s = 10; int main() {<!-- --> std::cout << U::s << std::endl; U u; // Use placement new and manual destruction new ( & amp;u.str) std::string("hello world"); std::cout << u.str << std::endl; u.str.~basic_string(); new ( & amp;u.vec) std::vector<int>(2, -1); u.vec.push_back(1); for (int i = 0; i < u.vec.size(); i + = 1) {<!-- --> std::cout << u.vec[i] << std::endl; } u.vec.~vector(); return 0; }
Use
std::variant – cppreference.com
ref example
std::variant is type-safe and can only access one type
#include <cassert> #include <string> #include <variant> int main() {<!-- --> std::variant<int, float> v, w; v = 12; // v contains int int i = std::get<int>(v); w = std::get<int>(v); w = std::get<0>(v); // Same effect as the previous line w = v; // Same effect as previous line // std::get<double>(v); // Error: no double in [int, float] // std::get<3>(v); // Error: legal subscript values are 0 and 1 try {<!-- --> std::get<float>(w); // w contains int instead of float: will throw } catch (const std::bad_variant_access & amp;) {<!-- --> } using namespace std::literals; std::variant<std::string> x("abc"); // The conversion constructor works when there is no ambiguity x = "def"; // Conversion assignment also works when there is no ambiguity std::variant<std::string, void const*> y("abc"); //Convert to void const * when passing char const * assert(std::holds_alternative<void const*>(y)); // Success y = "xyz"s; assert(std::holds_alternative<std::string>(y)); // Success }
Construction
Just assign the value directly.
Common structure
#include <iostream> #include <string> #include <variant> void fun() {<!-- --> // can be the same std::variant<int, int> var; // The operation is special } int main() {<!-- --> std::variant<std::string, int> empty; std::variant<std::string, int> var("abc"); std::cout << std::get<0>(var) << std::endl; var = 123; std::cout << std::get<1>(var) << std::endl; std::get<1>(var) = 654321; std::cout << std::get<1>(var) << std::endl; }
Leave blank
#include <iostream> #include <string> #include <variant> int main() {<!-- --> std::variant<int, std::string> v = "abc"; // v.index = 1 std::cout << "v.index = " << v.index() << '\\ '; // Empty v = {<!-- -->}; // v.index = 0 std::cout << "v.index = " << v.index() << '\\ '; }
emplace
Can be constructed in situ
#include <iostream> #include <string> #include <variant> int main() {<!-- --> std::variant<std::string> var; var.emplace<0>(2, 'a'); std::cout << std::get<0>(var) << std::endl; var.emplace<std::string>("Hello World"); std::cout << std::get<std::string>(var) << std::endl; }
monostate
There is a kind of special situation.
Intentionally a unit type used for well-behaved std::variant
hollow optionals. Specifically, a non-defaultable variant
can list std::monostate
as its first optional option: this makes the variant
itself defaultable structure.
- std::monostate
- valueless_by_exception()
#include <iostream> #include <variant> struct S {<!-- --> int value; S(int i) : value(i) {<!-- --> std::cout << __func__ << std::endl; } }; int main() {<!-- --> // This declaration will fail if there is no monostate type. // This is because S is not default constructible. std::variant<std::monostate, S> var; //Do not retain value std::cout << std::boolalpha << var.valueless_by_exception() << std::endl; // std::get<S> will throw an exception! We need to assign a value // var.index() is now 0 - first element std::cout << "cur index = " << var.index() << '\\ '; var = 12; std::cout << std::get<S>(var).value << '\\ '; }
Access
std::get<>
Please pay attention to similar situations
#include <iostream> #include <string> #include <variant> void fun0() {<!-- --> // can be the same std::variant<int, int> var; // Compilation fails, non-dynamic error // var = 10; // Compilation fails, non-dynamic error // std::get<int>(var) = 100; // Subscript access is possible std::get<0>(var) = 10; try {<!-- --> std::cout << std::get<1>(var) << std::endl; } catch (const std::bad_variant_access & amp; e) {<!-- --> // std::get: wrong index for variant std::cout << e.what() << std::endl; } } void fun1() {<!-- --> std::variant<std::string, int> var("abc"); std::cout << std::get<0>(var) << std::endl; std::cout << std::get<std::string>(var) << std::endl; } int main() {<!-- --> fun0(); fun1(); }
std::holds_alternative<>
Determine whether it can be converted
#include <iostream> #include <string> #include <variant> int main() {<!-- --> std::cout << std::boolalpha; std::variant<std::string, int> var("abc"); std::cout << "int " << std::holds_alternative<int>(var) << std::endl; std::cout << "string " << std::holds_alternative<std::string>(var) << std::endl; }
Get pointer std::get_if<>
#include <iostream> #include <variant> int main() {<!-- --> std::variant<int, float> var; std::cout << & amp;var << std::endl; // shaping var = 12; auto pval = std::get_if<int>( & amp;var); std::cout << pval << std::endl; if (pval) {<!-- --> std::cout << "variant value: " << *pval << '\\ '; } else {<!-- --> std::cout << "failed to get value!" << '\\ '; } //floating point number var = 12.3f; auto pval2 = std::get_if<float>( & amp;var); std::cout << pval2 << std::endl; if (pval2) {<!-- --> std::cout << "variant value: " << *pval2 << '\\ '; } else {<!-- --> std::cout << "failed to get value!" << '\\ '; } }
0xc10cbffba0 0xc10cbffba0 variant value: 12 0xc10cbffba0 variant value: 12.3
Get the optional number std::variant_size
determined in the compiler
- std::variant_size
- std::variant_size_v
#include <any> #include <cstdio> #include <variant> // all pass static_assert(std::variant_size_v<std::variant<>> == 0); static_assert(std::variant_size_v<std::variant<int>> == 1); static_assert(std::variant_size_v<std::variant<int, int>> == 2); static_assert(std::variant_size_v<std::variant<int, int, int>> == 3); static_assert(std::variant_size_v<std::variant<int, float, double>> == 3); // std::monostate is also a placeholder static_assert(std::variant_size_v<std::variant<std::monostate, void>> == 2); static_assert(std::variant_size_v<std::variant<const int, const float>> == 2); static_assert(std::variant_size_v<std::variant<std::variant<std::any>>> == 1); int main() {<!-- --> std::puts("All static assertions passed."); }