我正在尝试实现
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; };
并使此类正常工作(映射插入、从函数返回、作为参数传递等)。
阻止代码工作的最大问题是转发构造函数不受约束。 转发构造函数始终需要受到约束,因为它与复制和移动构造函数竞争。 事实上,在场景中:
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
。
隐式转换非常危险,我不会添加它们。特别是因为
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;
};