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

文章目录

  • 前言与需求
  • union
    • 内存映射图
    • C++11的union
  • 使用
    • ref示例
    • 构造
      • 普通构造
      • 置空
      • emplace
      • monostate
    • 访问
      • std::get<>
      • std::holds_alternative<>
      • 获取指针std::get_if<>
      • 获取可选数量个数std::variant_size
  • END

前言与需求

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

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

union

内存映射图

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

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

(C++17) variant的使用与union对比_第1张图片

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

C++11的union

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

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

#include 
#include 
#include 

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 
#include 
#include 

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(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 
#include 
#include 

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 
#include 
#include 

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 
#include 
#include 

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 
#include 

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

访问

std::get<>

请注意同类型的情况

#include 
#include 
#include 

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

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

    // 编译不通过,非动态错误
    // std::get(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 
#include 
#include 

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 
#include 

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 
#include 
#include 

// 全部 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

你可能感兴趣的:(C/C++,c++,开发语言,c语言)