C++17 introduces a new way of working with unions using the std::variant class template from a header. This class template offers a type-safe way of storing and accessing a union. To declare a variant using a std::variant, we would write:
#include
int main()
{
std::variant myvariant;
}
This example defines a variant that can hold three types. When we initialize or assign a value to a variant, an appropriate type is chosen. For example, if we initialize a variant with a character value, the variant will currently hold a char data member. Accessing other members at this point will throw an exception. Example:
#include
#include
int main()
{
std::variant myvariant{ 'a' }; // variant now holds // a char
std::cout << std::get<0>(myvariant) << '
'; // obtain a data member by // index
std::cout << std::get(myvariant) << '
'; // obtain a data member // by type
myvariant = 1024; // variant now holds an int
std::cout << std::get<1>(myvariant) << '
'; // by index
std::cout << std::get(myvariant) << '
'; // by type
myvariant = 123.456; // variant now holds a double
}
We can access a variant value by index using the std::get(variant_name) function. Or we can access the variant value by a type name using: std::get(variant_name). If we tried to access a wrong type or wrong index member, an exception of type const std::bad_variant_access& would be raised. Example:
#include
#include
int main()
{
std::variant myvariant{ 123 }; // variant now holds an int
std::cout << "Current variant: " << std::get(myvariant) << '
';
try
{
std::cout << std::get(myvariant) << '
'; // exception is // raised
}
catch (const std::bad_variant_access& ex)
{
std::cout << "Exception raised. Description: " << ex.what();
}
}
We define a variant that can hold either int or double. We initialize the variant with a 123 literal of type int. So now our variant holds an int data member. We can access that member using the index of 0 or a type name which we supply to the std::get function. Then we try to access the wrong data member of type double. An exception is raised. And the particular type of that exception is std::bad_variant_access. In the catch block, we handle the exception by parsing the parameter we named ex. A parameter is of type std::bad_variant_access, which has a .what() member function that provides a short description of the exception.
20.4 C++20
The C++ 20 standard promises to bring some big additions to the language. Its impact on the existing standards is said to be as big as the C++11 was to a C++98/C++03 standard. At the time of writing, the C++20 standard is to be ratified around May 2020. The full implementation and the support in the compilers should follow. Some of the following things may, at first glance, seem intimidating, especially when beginning C++. However, do not worry. At the time of writing, none of the compilers fully support the C++20 standard, but that is about to change. Once the compilers fully support the C++20 standard, trying out the examples will be much easier. With that in mind, let us go through some of the most exciting C++20 features.
20.1 Modules
Modules are the new C++20 feature, which aims to eliminate the need for the separation of code into header and source files. So far, in traditional C++, we have organized our source code using headers files and source files. We keep our declarations/interfaces in header files. We put our definitions/implementations in source files. For example, we have a header file with a function declaration:
mylibrary.h
#ifndef MYLIBRARY_H
#define MYLIBRARY_H
int myfunction();
#endif // !MYLIBRARY_H39
Here we declare a function called myfunction(). We surround the code with header guards, which ensures the header file is not included multiple times during the compilation. And we have a source file with the function definition. This source file includes our header file: