[英]boost::recursive_variant accepts map, but rejects unordered_map
[英]Using boost::recursive_variant with std::unordered_map
我正在尝试创建一个std::unordered_map
,其中boost::variant
作为键,integer 作为值。 我正在努力使这项工作:
#include <boost/variant.hpp>
#include <unordered_map>
#include <string>
enum Token {};
struct AssignExpr;
struct LiteralExpr;
using Expr = boost::variant<boost::blank,
boost::recursive_wrapper<AssignExpr>,
boost::recursive_wrapper<LiteralExpr>>;
using Literal = int;
struct LiteralExpr {
Literal literal;
explicit LiteralExpr(const Literal &literal);
};
struct AssignExpr {
Token name;
Expr value;
AssignExpr(const Token &name, const Expr &val);
};
int main() {
LiteralExpr li{0};
AssignExpr ae{{}, li};
std::unordered_map<Expr, std::size_t> m_locals{{li, 10}, {ae, 20}};
}
我尝试添加operator==
和 hash function但出现链接错误。
如果您使用的是基于散列的容器,则所有元素类型都必须是可散列的并且具有相等性可比性。
Boost 变体根据 ADL 自定义点hash_value
实现哈希 - 默认情况下使用boost::hash<T>{}
。 这意味着获得可哈希性的最简单方法是为所有类型实现hash_value
- 包括boost::blank
。
接下来,让我们默认operator==
bool operator==(LiteralExpr const&) const = default;
bool operator==(AssignExpr const&) const = default;
或者,如果您的编译器足够时髦,可以在一个 go 中进行三向比较:
auto operator<=>(LiteralExpr const&) const = default;
auto operator<=>(AssignExpr const&) const = default;
然后它全部编译,并且更好地运行而不会引起 UB。 请确保您的 hash 同意平等关系!
#include <boost/variant.hpp>
#include <boost/functional/hash.hpp>
#include <string>
#include <unordered_map>
#include <utility>
namespace boost {
[[maybe_unused]] static inline size_t hash_value(boost::blank) { return 0; }
} // namespace boost
namespace MyLib {
using boost::hash_value;
enum Token {A,B,C,D,E,F,G,H};
[[maybe_unused]] static constexpr auto name_of(Token tok) {
return tok["ABCDEFGH"];
}
struct AssignExpr;
struct LiteralExpr;
using Expr = boost::variant< //
boost::blank, //
boost::recursive_wrapper<AssignExpr>, //
boost::recursive_wrapper<LiteralExpr>>; //
using Literal = int;
struct LiteralExpr {
Literal literal;
explicit LiteralExpr(Literal literal) : literal(literal) {}
auto operator<=>(LiteralExpr const&) const = default;
friend size_t hash_value(LiteralExpr const& le) {
return hash_value(le.literal);
}
friend std::ostream& operator<<(std::ostream& os, LiteralExpr const& le) {
return os << le.literal;
}
};
struct AssignExpr {
Token name;
Expr value;
AssignExpr(Token name, Expr val) : name(name), value(std::move(val)) {}
auto operator<=>(AssignExpr const&) const = default;
friend size_t hash_value(AssignExpr const& ae) {
auto h = hash_value(ae.name);
boost::hash_combine(h, hash_value(ae.value));
return h;
}
friend std::ostream& operator<<(std::ostream& os, AssignExpr const& ae) {
return os << name_of(ae.name) << " := " << ae.value;
}
};
} // namespace MyLib
#define FMT_DEPRECATED_OSTREAM
#include <fmt/ranges.h>
#include <fmt/ostream.h>
template <> struct fmt::formatter<MyLib::Expr> {
auto parse(auto& ctx) const { return ctx.begin(); }
auto format(MyLib::Expr const& e, auto& ctx) const {
return boost::apply_visitor(
[&](auto const& el) { return format_to(ctx.out(), "Expr({})", el); },
e);
}
};
int main() {
using namespace MyLib;
LiteralExpr li{42};
AssignExpr ae{D, li};
std::unordered_map<Expr, std::size_t> m_locals{{li, 10}, {ae, 20}};
fmt::print("{}\n", fmt::join(m_locals, "\n"));
}
印刷
(Expr(D := 42), 20)
(Expr(42), 10)
我会简化。 LiteralExpr
不会在Literal
上添加任何内容(当然也不会递归)。
除非您真的需要该功能(在您的 AST 中通常类似于Null
),否则请跳过blank
。
您所有的结构似乎都可以是聚合体,所以也许只是享受聚合体构造。
struct Nil {
friend constexpr size_t hash_value(Nil) { return 0; }
constexpr bool operator==(Nil) const { return true; }
};
using Literal = int;
struct AssignExpr;
using Expr = boost::variant< //
Nil, //
Literal, //
boost::recursive_wrapper<AssignExpr>>; //
enum Token {A,B,C,D,E,F,G,H};
struct AssignExpr {
Token name;
Expr value;
};
您可能会稍微同化相等和 hash 操作:
auto key() const { return std::tie(name, value); }
bool operator==(AssignExpr const& rhs) const { return key() == rhs.key(); }
friend size_t hash_value(AssignExpr const& ae) { return hash_value(ae.key()); }
这使得两者不能不同步。
现在它变成了Live Demo ,缩短了 25 行并且功能更强大:
(Expr(Nil), 30)
(Expr(D := 42), 20)
(Expr(42), 10)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.