中文
std::variant & std::visit

std::variant & std::visit

关于 C++17 中新增的 variant 和 visit 的内容总结

其他信息

[相关文档](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0088r3.html

)

上述链接中介绍了从 C++17 开始新增的 Variant 及其添加原因。简而言之,由于 Boost 库中相关功能已被使用太久,因此有人提议不要再拖延,应尽快将其纳入标准,将其作为 std::optional 引入。

内部结构

1. Variant

  • 数据缓冲区 用于存储模板参数中最大类型所需的足够大内存空间,通常通过 Union 实现

  • 标识符 用于表示当前数据缓冲区中存储了何种类型的对象的索引或标识符。通过使用该标识符,std::variant 能够保证类型安全性

凭借充足的内存空间,variant 类型能够实现如下行为。


	struct Tracer
	{
		std::string name;

		Tracer(const std::string& n) : name(n)
		{
			std::cout << "  [+] '" << name << "' Tracer created (Constructor)\n";
		}

		// Copy Constructor
		Tracer(const Tracer& other) : name(other.name)
		{
			std::cout << "  [*] '" << name << "' Tracer copy-constructed (Copy Constructor)\n";
		}

		~Tracer()
		{
			std::cout << "  [-] '" << name << "' Tracer destroyed (Destructor)\n";
		}
	};

	// 1. Initialize the variant with a Tracer type.
	std::cout << "1. Initializing variant with Tracer(\"Apple\").\n";
	std::variant<int, Tracer> var = Tracer("Apple");
	std::cout << "   Variant now holds Tracer(\"Apple\").\n\n";

	// 2. Assign a value of a different type (int).
	std::cout << "2. Assigning integer 100 to the variant.\n";
	var = 100;
	std::cout << "   Variant now holds integer 100.\n\n";

	// 3. Assign another Tracer type value again.
	std::cout << "3. Assigning Tracer(\"Banana\") to the variant.\n";
	var = Tracer("Banana");
	std::cout << "   Variant now holds Tracer(\"Banana\").\n\n";

	std::cout << "4. main function is about to end.\n";
	return 0;

执行此类代码将得到如下结果。

1. Initializing variant with Tracer("Apple").
  [+] 'Apple' Tracer created (Constructor)
  [*] 'Apple' Tracer copy-constructed (Copy Constructor)
  [-] 'Apple' Tracer destroyed (Destructor)
   Variant now holds Tracer("Apple").

2. Assigning integer 100 to the variant.
  [-] 'Apple' Tracer destroyed (Destructor)
   Variant now holds integer 100.

3. Assigning Tracer("Banana") to the variant.
  [+] 'Banana' Tracer created (Constructor)
  [*] 'Banana' Tracer copy-constructed (Copy Constructor)
  [-] 'Banana' Tracer destroyed (Destructor)
   Variant now holds Tracer("Banana").

4. main function is about to end.
  [-] 'Banana' Tracer destroyed (Destructor)

可以观察到,先调用原有值的析构函数,随后在栈上创建临时对象,最后将复制生成的值移入自身内存缓冲区。

因此,如果内部通过联合体(union)保留存储最大尺寸类的空间,无论variant类型中传入什么,都能确保其正常使用!

2. 访问机制

虽然不同库的实现各异,

  • 函数指针表

  • Switch 语句

但通常可分为这两种形式。实际上,除了实现方式、开销以及编译器优化方面存在些许差异外,基本都可以理解为:通过检查标识符,根据激活的类型调用相应的函数。

댓글 작성

게시글에 대한 의견을 남겨 주세요.

댓글 0