(C++17) variant的使用与union对比

前言与需求

联合体,是在C语言时代就存在的概念。主要应用在一些收内存限制较大的情景。

但是传统C的限制太大,但是到了C++中给出了更安全的类型std::variant

union

内存映射图

先来看一张C语言数据结构内存映射图

!网图(非原创),侵删!

在这里插入图片描述

在union中所有字段是公用一块内存区域的。

C++11的union

在旧时union中成员不能是一个非凡类型。到了C++11解除了限制,只要是非应用类型即可。

其实union也可以单独开一篇文章来讲,这里给个简单示例,不做过多讲解:

#include <iostream>
#include <string>
#include <vector>

union U {
    U() {
    }
    ~U() {
    }

    static int s;

    int   x;
    float y;

    std::string      str;
    std::vector<int> vec;
};
// 同类的静态成员类似
int U::s = 10;

int main() {
    std::cout << U::s << std::endl;

    U u;

    // 使用 placement new 和 手动析构
    new (&u.str) std::string("hello world");
    std::cout << u.str << std::endl;
    u.str.~basic_string();

    new (&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;
}

使用

std::variant - cppreference.com

ref示例

std::variant 是类型安全的,只能访问一种类型

#include <cassert>
#include <string>
#include <variant>

int main() {
    std::variant<int, float> v, w;
    v     = 12;  // v 含 int
    int i = std::get<int>(v);
    w     = std::get<int>(v);
    w     = std::get<0>(v);  // 与前一行效果相同
    w     = v;               // 与前一行效果相同

    //  std::get<double>(v); // 错误: [int, float] 中无 double
    //  std::get<3>(v);      // 错误:合法下标值为 0 与 1

    try {
        std::get<float>(w);  // w 含 int 而非 float :将抛出
    } catch (const std::bad_variant_access&) {
    }

    using namespace std::literals;

    std::variant<std::string> x("abc");  // 转换构造函数在无歧义时起作用
    x = "def";  // 转换赋值在无歧义时亦起作用

    std::variant<std::string, void const*> y("abc");
    // 传递 char const * 时转换成 void const *
    assert(std::holds_alternative<void const*>(y));  // 成功
    y = "xyz"s;
    assert(std::holds_alternative<std::string>(y));  // 成功
}

构造

直接赋值即可。

普通构造

#include <iostream>
#include <string>
#include <variant>

void fun() {
    // 可以相同
    std::variant<int, int> var;
    // 操作比较特殊
}

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;
}

置空

#include <iostream>
#include <string>
#include <variant>

int main() {
    std::variant<int, std::string> v = "abc";
    // v.index = 1
    std::cout << "v.index = " << v.index() << '\n';

    // 置空了
    v = {};
    // v.index = 0
    std::cout << "v.index = " << v.index() << '\n';
}

emplace

可以原地构造

#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

有一类情况比较特殊。

有意为行为良好的 std::variant 中空可选项所用的单位类型。具体而言,非可默认构造的 variant 可以列 std::monostate 为其首个可选项:这使得 variant 自身可默认构造。

  • 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() {
    // 若无 monostate 类型则此声明将失败。
    // 这是因为 S 不可默认构造。

    std::variant<std::monostate, S> var;
    // 不保有值
    std::cout << std::boolalpha << var.valueless_by_exception() << std::endl;

    // std::get<S> 将抛异常!我等需要赋一个值
    // var.index() 现为 0 ——首个元素
    std::cout << "cur index = " << var.index() << '\n';
    var = 12;
    std::cout << std::get<S>(var).value << '\n';
}

访问

std::get<>

请注意同类型的情况

#include <iostream>
#include <string>
#include <variant>

void fun0() {
    // 可以相同
    std::variant<int, int> var;

    // 编译不通过,非动态错误
    // var = 10;

    // 编译不通过,非动态错误
    // std::get<int>(var) = 100;

    // 下标访问可行
    std::get<0>(var) = 10;

    try {
        std::cout << std::get<1>(var) << std::endl;
    } catch (const std::bad_variant_access& 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<>

判断是否可以转换

#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;
}

获取指针std::get_if<>

#include <iostream>
#include <variant>

int main() {
    std::variant<int, float> var;
    std::cout << &var << std::endl;

    // 整形
    var       = 12;
    auto pval = std::get_if<int>(&var);
    std::cout << pval << std::endl;
    if (pval) {
        std::cout << "variant value: " << *pval << '\n';
    } else {
        std::cout << "failed to get value!" << '\n';
    }

    // 浮点数
    var        = 12.3f;
    auto pval2 = std::get_if<float>(&var);
    std::cout << pval2 << std::endl;
    if (pval2) {
        std::cout << "variant value: " << *pval2 << '\n';
    } else {
        std::cout << "failed to get value!" << '\n';
    }
}
0xc10cbffba0
0xc10cbffba0
variant value: 12
0xc10cbffba0
variant value: 12.3

获取可选数量个数std::variant_size

在编译器确定

  • std::variant_size
  • std::variant_size_v
#include <any>
#include <cstdio>
#include <variant>

// 全部 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 也算一个占位
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.");
}



END

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
If you’ve ever asked “what’s in C++17 and what does it mean for me and my code?” — and I hope you have — then this book is for you. Now that the C++ standard is being released regularly every three years, one of the challenges we have as a community is learning and absorbing the new features that are being regularly added to the standard language and library. That means not only knowing what those features are, but also how to use them effectively to solve problems. Bartlomiej Filipek does a great job of this by not just listing the features, but explaining each of them with examples, including a whole Part 3 of the book about how to apply key new C++17 features to modernize and improve existing code — everything from upgrading enable_if to the new if constexpr, to refactoring code by applying the new optional and variant vocabulary types, to writing parallel code using the new standard parallel algorithms. In each case, the result is cleaner code that’s often also significantly faster too. The point of new features isn’t just to know about them for their own sake, but to know about how they can let us express our intent more clearly and directly than ever in our C++ code. That ability to directly “say what we mean” to express our intent, or to express “what” we want to achieve rather than sometimes-tortuous details of “how” to achieve it through indirect mechanisms, is the primary thing that determines how clean and writable and readable — and correct — our code will be. For C++ programmers working on real-world projects using reasonably up-to-date C++ compilers, C++17 is where it’s at in the industry today for writing robust production code. Knowing what’s in C++17 and how to use it well is an important tool that will elevate your day-to-day coding, and more likely than not reduce your day-to-day maintenance and debugging chores. If you’re one of the many who have enjoyed Barteks’s blog (bfilipek.com, frequently cited at isocpp.org), you’ll certainly also enjoy this entertaining and informative book. And if you haven’t enjoyed his blog yet, you should check it out too… and then enjoy the book.

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值