How to implement assignment operators for a type that uses "std::variant"?

  assignment-operator, c++, c++17, variant

I’m replacing an implementation of a Variant (based on from Poco) with the std::variant from c++17. With the following code, I am able to convert a Variant object to any of the types the std::variant holds, where appropriate. However, I am having difficulty going in the other direction – i.e. how to to implicitly cast other types to my Variant.


#ifndef MINIMALVARIANT
#define MINIMALVARIANT

#include <iostream>
#include <utility>
#include <variant>
#include "gtest/gtest.h"

using variant_t = std::variant<std::string, bool, std::int32_t, std::uint32_t>;

class Variant {
public:
    explicit Variant(variant_t value)
            : variant_(std::move(value)) {};

    Variant() = default;

    template<class T>
    T *get_if() {
        return std::get_if<T>(&variant_);
    }

    template<typename T>
    operator T() const {
        return std::visit(
                [&](auto const &val) {
                    if constexpr (std::is_convertible_v<decltype(val), T>) {
                        return T(val);
                    } else {
                        // T is not supported conversion type from typeid(value_)
                        throw std::bad_variant_access{};
                        return T{}; // oddly enough, this *is* necessary
                    }
                }, variant_);
    }

    bool operator==(const Variant &rhs) const {
        return variant_ == rhs.variant_;
    }

    bool operator!=(const Variant &rhs) const {
        return !(rhs == *this);
    }

    Variant &operator=(const Variant &setting) {
        if (*this != setting) {
            variant_ = setting.variant_;
        };
        return *this;
    }

    Variant &operator=(Variant &&setting) noexcept {
        if (*this != setting) {
            variant_ = std::move(setting.variant_);
        };
        return *this;
    }

private:
    variant_t variant_;
};


#endif // MINIMALVARIANT

The behaviour I want is pretty much summed up in the following tests, if they are indeed possible (they are with the Poco implementation, so I assume they are with the STL implementation).

#include "gtest/gtest.h"
#include "MinimalVariant.h"

TEST(VariantTests, ImplicitConvertVariantToType){
    Variant v(1234);
    unsigned int u = v; // okay
    ASSERT_EQ(u, 1234); // pass
}

TEST(VariantTests, ImplicitConvertTypeToVariant){
    unsigned int u = 1234;
    Variant v;
    // v = u; // error, assignment operators do not work
}

bool funcThatTakesAVariant(Variant v){
    if (bool x = v.get_if<bool>()){
        return x;
    } else {
        return false;
    }
}

TEST(VariantTests, ImplicitConvertFuncParameter){
    // ASSERT_TRUE(funcThatTakesAVariant(true)); // error, no implicit cast from bool to Variant
}

Variant funcThatReturnsAVariant(bool truth){
    return truth; // compile error
}

TEST(VariantTests, ImplicitConvertReturnType){
    Variant boolVariant = funcThatReturnsAVariant(true);
}

}

TEST(VariantTests, ImplicitConvertTypeToVariant){
    unsigned int u = 1234;
    Variant v;
    v = u;
}

At present, I’ve tried templating the assignment operators and a templated constructor, but so far everything I’ve tried has fallen apart.

Source: Windows Questions C++

LEAVE A COMMENT