如何实现 std::variant 包装类的隐式转换和运算符?

问题描述 投票:0回答:2

我正在尝试实现

std::variant
包装器,在分配和创建方面其行为与
std::variant
类似,但也允许隐式转换为变体持有的所有替代方案。

此实现给了我错误,并尝试使用

operator ()
使 Variant 脱离 Variant。

class Settings
{
public:

    class Variant
    {
    public:
        using variant_t = std::variant<std::monostate, int, float, bool, std::string>;

        Variant() = default;

        template <typename T>
        Variant(T&& v) : value(v) {}

        template <typename T>
        operator T() const
        {
            if (std::holds_alternative<T>(value)) {
                return std::get<T>(value);
            }
            return T();
        }

        template <typename T>
        constexpr T get() const
        {
            return static_cast<T>(this);
        }

        bool        GetBool() const { return get<bool>(); };
        float       GetFloat() const { return get<float>(); };
        int         GetInt() const { return get<int>(); };
        std::string GetString() const { return get<std::string>(); };

    private:
        variant_t value;
    };

    Variant GetValue(std::string_view key) const
    {
        std::shared_lock lock(mtx);
        auto iter = data.find(key);
        return iter == data.end() ? Variant() : iter->second;
    };

    void SetValue(std::string_view key, Variant value)
    {
        std::unique_lock lock(mtx);
        data.insert_or_assign(key, value);
    };

private:
    using SettingsMap = std::unordered_map<std::string_view, Variant>;

    Settings();
    ~Settings() {};

    mutable std::shared_mutex mtx;
    SettingsMap data;
};

需要更改什么才能允许这样的分配/转换:

Variant floatSetting = 0.5f;
float fVar = floatSetting;

Variant boolSetting = true;
if (boolSetting) { return true; };

并使此类正常工作(映射插入、从函数返回、作为参数传递等)。

c++ templates type-conversion c++17 variant
2个回答
1
投票

阻止代码工作的最大问题是转发构造函数不受约束。 转发构造函数始终需要受到约束,因为它与复制和移动构造函数竞争。 事实上,在场景中:

Variant a = /* ... */;
Variant b = a;

...转发构造函数在重载解析中胜过复制和移动构造函数。 这种情况也发生在

GetValue

因此,你需要这样写:

template <typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, Variant>, int> = 0>
Variant(T&& v) : value(v) {}

拥有不受约束的转换运算符模板也非常可疑。 您的运算符

T
适用于
T = variant_t
等情况,或任何不在变体中的任意类型。 您应该确保此模板不能用于此类情况,其中:

template <typename T, std::enable_if_t<!std::is_same_v<std::decay_t<T>, variant_t>, int> = 0>
operator T() const

但是,即使有效,我也不会这样做。 隐式转换通常是不受欢迎的。 它们在这里特别糟糕,因为

std::get
会抛出异常,所以看起来很无辜的代码就像

float fVar = floatSetting;

...实际上可以扔

std::bad_variant_access


0
投票

隐式转换非常危险,我不会添加它们。特别是因为

GetValue
可能无法找到设置。如果您尝试转换它,您返回的默认
Variant
将抛出异常。

你是否需要向外界暴露

Variant
?想必这些设置的用户知道他们期望从给定的密钥中获得什么类型,您可以要求他们在
GetValue
中指定它。

class Settings
{
    using variant_t = std::variant<std::monostate, int, float, bool, std::string>;
public:

    template <typename T>
    struct Key
    {
        std::string_view key;
    };

    template <typename T>
    std::optional<T> GetValue(Key<T> key) const
    {
        std::shared_lock lock(mtx);
        auto iter = data.find(key.key);
        return iter == data.end() ? std::nullopt : iter->second.get<T>();
    };

    template <typename T>
    void SetValue(Key<T> key, T value)
    {
        std::unique_lock lock(mtx);
        data.insert_or_assign(key.key, value);
    };

private:
    using SettingsMap = std::unordered_map<std::string_view, variant_t>;

    Settings();
    ~Settings() {};

    mutable std::shared_mutex mtx;
    SettingsMap data;
};
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.