New-Style Union: std::variant

Problems With C Unions, And C++ Types

  • union with C++ can be gotten right

  • Need to call destructors explicitly

  • Other weirdnesses

  • no!

  • std::variant

Instantiation, Default Initialization, Member Access

#include <gtest/gtest.h>
#include <variant>

TEST(variant_suite, default_ctor)
{
    std::variant<int, float> v;                        // <--- default: int{}

    ASSERT_EQ(std::get<int>(v), 0);                    // <--- by-type: does not fail
    ASSERT_EQ(std::get<0>(v), 0);                      // <--- by-position
    ASSERT_EQ(v.index(), 0);                           // <--- which position does it hold?
    ASSERT_TRUE(std::holds_alternative<int>(v));       // <--- which type does it hold?

    v = 36.5f;                                         // <--- now has float
                                                       // ...
    ASSERT_FLOAT_EQ(std::get<float>(v), 36.5);
    ASSERT_FLOAT_EQ(std::get<1>(v), 36.5);
    ASSERT_EQ(v.index(), 1);
    ASSERT_TRUE(std::holds_alternative<float>(v));
}

Non-Default Initialization

#include <gtest/gtest.h>
#include <variant>

TEST(variant_suite, converting_ctor)
{
    std::variant<int, float> v{36.5f};                 // <--- holds float

    ASSERT_FLOAT_EQ(std::get<float>(v), 36.5);
    ASSERT_FLOAT_EQ(std::get<1>(v), 36.5);
    ASSERT_EQ(v.index(), 1);
}

Accessing Non-Current Member: std::bad_variant_access

#include <gtest/gtest.h>
#include <variant>

TEST(variant_suite, bad_access)
{
    std::variant<int, float> v{42};

    try {
        std::get<float>(v);
    }
    catch (const std::bad_variant_access&) {}
}
#include <gtest/gtest.h>
#include <variant>

struct Visitor
{
    Visitor() : int_seen{false}, float_seen{false} {}
    void operator()(int) { int_seen = true; }
    void operator()(float) { float_seen = true; }
    bool int_seen, float_seen;
};

TEST(variant_suite, visit)
{
    std::variant<int, float> v;

    Visitor vis;

    std::visit(vis, v);
    ASSERT_TRUE(vis.int_seen);

    v = 36.5f;
}

Non-Throwing Accessor: std::get_if<>

  • Exceptions are bad (at least in Embedded)

  • ⟶ back to pointers 🤘

#include <gtest/gtest.h>
#include <variant>

TEST(variant_suite, get_if)
{
    int tmp[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    (void)tmp;
    std::variant<int, float> v{42};

    const int* pi = std::get_if<int>(& /*REALLY!*/ v);
    ASSERT_NE(pi, nullptr);
    ASSERT_EQ(*pi, 42);

    const float* pf = std::get_if<float>(&v);
    ASSERT_EQ(pf, nullptr);
}

And Non-Fundamental Types?

#include <gtest/gtest.h>
#include <string>
#include <variant>

TEST(variant_suite, string_charp)
{
    std::variant<std::string, const char*> v;
    ASSERT_EQ(std::get<std::string>(v), std::string());

    const char* s = "blah";
    v = s;
    ASSERT_EQ(std::get<const char*>(v), s);

    v = "blah";
    ASSERT_EQ(std::get_if<std::string>(&v), nullptr);
}
#include <gtest/gtest.h>
#include <variant>

struct Foo
{
    Foo() = delete;
    Foo(void*) {}
};

TEST(variant_suite, no_default_ctor)
{
    // std::variant<Foo> v;                            // <--- error: no default ctor
    std::variant<Foo> v{nullptr};                      // <--- OK

    (void)v;
}