是否可以使用 std::variant of std::variants
Is it ok to use std::variant of std::variants
我试图将两个变体合并为一个变体只是为了便于阅读。这是代码:
using VariantType_basic = std::variant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, std::string>;
using VariantType_vector = std::variant<vector<int8_t>, vector<uint8_t>, vector<int16_t>, vector<uint16_t>, vector<int32_t>, vector<uint32_t>, vector<int64_t>, vector<uint64_t>, vector<std::string>>;
using VariantType_all = std::variant<VariantType_basic, VariantType_vector>;
class Container {
public:
Container(){}
template<typename T>
T get(string key, bool &found){
found = false;
T result;
auto elem = m_internal_map.find(key);
if(elem != m_internal_map.end())
std::visit(
[&](const auto& v){
// if(holds_alternative<T>(v)){
result = std::get<T>(v);
found = true;
//}
},
elem->second
);
return result;
}
template<typename T>
void put(string key, T&elem){
}
private:
map<string, VariantType_all> m_internal_map;
};
当我尝试执行如下操作时,get() 方法在 result = std::get<T>(v);
编译时失败:
Container cont;
bool found;
cont.get<uint16_t>("www", found);
错误消息很大,但第一条错误消息是这样的:/usr/include/c++/7/variant:762:7: error: static assertion failed: T should occur for exactly once in alternatives
我应该停止尝试使用变体的变体吗?
问题是 std::visit
会在编译时为 all 变体类型实例化对你的 lambda 的调用,然后只选择与你的实际值相对应的那个运行时的变体。按照当前编写 lambda 的方式,这意味着它将尝试实例化
result = std::get<T>(v);
对于您给定的 T
,VariantType_basic
和 VariantType_vector
的 lambda 版本。由于您的 VariantType_vector
根本不包含 std::uint16_t
,因此第二个实例化将无法编译并出现给定错误,因为您试图在不包含 [的类型上调用 std::get<T>
=14=] 在备选方案列表中……
解决此问题的一种方法是编写您的访问者 lambda,以便调用 std::get<T>
的代码仅针对变体值实例化,这些变体实际上可能包含您正在使用的类型的值寻找:
template <typename T, typename V>
constexpr bool has_variant = false;
template <typename T, typename... Types>
constexpr bool has_variant<T, std::variant<Types...>> = (std::is_same_v<T, Types> || ...);
template <typename T, typename V>
constexpr bool has_variant<T, V&> = has_variant<T, V>;
然后
std::visit([&](const auto& v)
{
if constexpr (has_variant<decltype(v), T>)
{
result = std::get<T>(v);
found = true;
}
else
found = false;
}, elem->second);
综上所述,在我看来,对于一个可能可以用不同方式更好地解决的问题,整个构造在我看来是一个非常脆弱的解决方案。我建议在这里重新考虑你的方法......
您可以使用 visit
的第二层来进入您的嵌套变体:
template < typename T >
struct Getter
{
T& value;
bool& found;
void operator()(const T& t)
{
value = t;
found = true;
}
template < typename U >
void operator()(const U& u) {}
};
if(elem != m_internal_map.end()) {
Getter< T > getter = { result, found };
std::visit( getter, elem->second );
}
如果类型不是所需类型,则 Getter
将忽略它。请注意,Getter
还将捕获可转换为 T
的值,因此可能不是您想要的。
使用单一级别的变体肯定会更简单。
我建议 "flatten" 变体而不是变体的变体:
template <typename Var1, typename Var2> struct variant_flat;
template <typename ... Ts1, typename ... Ts2>
struct variant_flat<std::variant<Ts1...>, std::variant<Ts2...>>
{
using type = std::variant<Ts1..., Ts2...>;
};
using VariantType_basic = std::variant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, std::string>;
using VariantType_vector = std::variant<std::vector<int8_t>, std::vector<uint8_t>, std::vector<int16_t>, std::vector<uint16_t>, std::vector<int32_t>, std::vector<uint32_t>, std::vector<int64_t>, std::vector<uint64_t>, std::vector<std::string>>;
using VariantType_all = variant_flat<VariantType_basic, VariantType_vector>::type;
而且你的 class 只需要处理一个变体级别:
class Container
{
public:
Container(){}
template<typename T>
T get(const std::string& key, bool &found) const {
found = false;
T result;
auto elem = m_internal_map.find(key);
if (elem != m_internal_map.end() && std::holds_alternative<T>(elem->second)){
result = std::get<T>(elem->second);
found = true;
}
return result;
}
template<typename T>
void put(const std::string& key, const T& elem) {
m_internal_map[key] = elem;
}
private:
std::map<std::string, VariantType_all> m_internal_map;
};
我试图将两个变体合并为一个变体只是为了便于阅读。这是代码:
using VariantType_basic = std::variant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, std::string>;
using VariantType_vector = std::variant<vector<int8_t>, vector<uint8_t>, vector<int16_t>, vector<uint16_t>, vector<int32_t>, vector<uint32_t>, vector<int64_t>, vector<uint64_t>, vector<std::string>>;
using VariantType_all = std::variant<VariantType_basic, VariantType_vector>;
class Container {
public:
Container(){}
template<typename T>
T get(string key, bool &found){
found = false;
T result;
auto elem = m_internal_map.find(key);
if(elem != m_internal_map.end())
std::visit(
[&](const auto& v){
// if(holds_alternative<T>(v)){
result = std::get<T>(v);
found = true;
//}
},
elem->second
);
return result;
}
template<typename T>
void put(string key, T&elem){
}
private:
map<string, VariantType_all> m_internal_map;
};
当我尝试执行如下操作时,get() 方法在 result = std::get<T>(v);
编译时失败:
Container cont;
bool found;
cont.get<uint16_t>("www", found);
错误消息很大,但第一条错误消息是这样的:/usr/include/c++/7/variant:762:7: error: static assertion failed: T should occur for exactly once in alternatives
我应该停止尝试使用变体的变体吗?
问题是 std::visit
会在编译时为 all 变体类型实例化对你的 lambda 的调用,然后只选择与你的实际值相对应的那个运行时的变体。按照当前编写 lambda 的方式,这意味着它将尝试实例化
result = std::get<T>(v);
对于您给定的 T
,VariantType_basic
和 VariantType_vector
的 lambda 版本。由于您的 VariantType_vector
根本不包含 std::uint16_t
,因此第二个实例化将无法编译并出现给定错误,因为您试图在不包含 [的类型上调用 std::get<T>
=14=] 在备选方案列表中……
解决此问题的一种方法是编写您的访问者 lambda,以便调用 std::get<T>
的代码仅针对变体值实例化,这些变体实际上可能包含您正在使用的类型的值寻找:
template <typename T, typename V>
constexpr bool has_variant = false;
template <typename T, typename... Types>
constexpr bool has_variant<T, std::variant<Types...>> = (std::is_same_v<T, Types> || ...);
template <typename T, typename V>
constexpr bool has_variant<T, V&> = has_variant<T, V>;
然后
std::visit([&](const auto& v)
{
if constexpr (has_variant<decltype(v), T>)
{
result = std::get<T>(v);
found = true;
}
else
found = false;
}, elem->second);
综上所述,在我看来,对于一个可能可以用不同方式更好地解决的问题,整个构造在我看来是一个非常脆弱的解决方案。我建议在这里重新考虑你的方法......
您可以使用 visit
的第二层来进入您的嵌套变体:
template < typename T >
struct Getter
{
T& value;
bool& found;
void operator()(const T& t)
{
value = t;
found = true;
}
template < typename U >
void operator()(const U& u) {}
};
if(elem != m_internal_map.end()) {
Getter< T > getter = { result, found };
std::visit( getter, elem->second );
}
如果类型不是所需类型,则 Getter
将忽略它。请注意,Getter
还将捕获可转换为 T
的值,因此可能不是您想要的。
使用单一级别的变体肯定会更简单。
我建议 "flatten" 变体而不是变体的变体:
template <typename Var1, typename Var2> struct variant_flat;
template <typename ... Ts1, typename ... Ts2>
struct variant_flat<std::variant<Ts1...>, std::variant<Ts2...>>
{
using type = std::variant<Ts1..., Ts2...>;
};
using VariantType_basic = std::variant<int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t, int64_t, uint64_t, std::string>;
using VariantType_vector = std::variant<std::vector<int8_t>, std::vector<uint8_t>, std::vector<int16_t>, std::vector<uint16_t>, std::vector<int32_t>, std::vector<uint32_t>, std::vector<int64_t>, std::vector<uint64_t>, std::vector<std::string>>;
using VariantType_all = variant_flat<VariantType_basic, VariantType_vector>::type;
而且你的 class 只需要处理一个变体级别:
class Container
{
public:
Container(){}
template<typename T>
T get(const std::string& key, bool &found) const {
found = false;
T result;
auto elem = m_internal_map.find(key);
if (elem != m_internal_map.end() && std::holds_alternative<T>(elem->second)){
result = std::get<T>(elem->second);
found = true;
}
return result;
}
template<typename T>
void put(const std::string& key, const T& elem) {
m_internal_map[key] = elem;
}
private:
std::map<std::string, VariantType_all> m_internal_map;
};