gpt4 book ai didi

c++ - 如果我的键是我的值的一部分,我应该使用映射还是集合?

转载 作者:太空狗 更新时间:2023-10-29 20:44:38 24 4
gpt4 key购买 nike

在 C++ 中,我有一个按名称排序的类,它是 std::string .我希望在 std::map 中每个唯一名称只有一个或 std::set .

我可以使用 std::setoperator<将按名称对我的实例进行排序,但是,我需要按名称查找实例。使用一个键是名称的映射是直接的,但是,我也可以使用一个集合并构造一个我的类的虚拟实例,其中包含我希望查找的名称以在集合中找到给定类的实际实例姓名。

我想我应该直接使用 map 来使代码直截了当,但想知道是否有一种方法可以使用集合,因为无论如何 key 实际上是我的对象的一部分,从而避免了一些冗余。

有没有一种方法可以使用该集合并能够以干净的方式通过键定位对象,或者我应该只使用 map 并完成它?

这里是要插入的类(以草稿形式),在每个目录中都有一组或映射节点名称:

class Node {
public:
Node(Directory &parent, const std::string &name)
: _name(name),
_parent(&parent),
_isRoot(false) {
if (name.empty()) {
throw InvalidNodeNameError(name);
}
}

protected:
// This is only used for the root directory:
Node()
: _name(""),
_parent(0),
_isRoot(true) {
}
Node(const std::string &name)
: _name(name),
_parent(0),
isRoot(false) {
}

public:
virtual ~Node() {
if (parent()) {
parent()->remove(*this);
}
}

bool operator<(const Node &rhs) const {
return _name < rhs._name;
}

Directory *parent() const {
return _parent;
}
void setParent(Directory *parent) {
_parent = parent;
}

const std::string &name() const {
return _name;
}

bool isRoot() const {
return _isRoot;
}

std::string pathname() const {
std::ostringstream path;

if (parent()) {
path << parent()->pathname() << '/';
} else {
path << '/';
}
path << name();

return path.str();
}

private:
// Not defined:
Node(const Node &rhs);
Node &operator=(const Node &rhs);

private:
std::string _name;
Directory *_parent;
const bool _isRoot;

};

最佳答案

实际上,您可以只使用 map ,但需要一个额外的指针,但我想您可能知道这一点,并且需要一些努力才能获得您想要的东西。

我一直认为 std::set 没有带有显式 KeyExtractor 模板参数是一个真正的痛苦,特别是因为我看到的每个实现都使用其中一个来避免重复代码在(多) map 和(多)集之间。这是一个快速而肮脏的 hack,尚未完成,它公开了 GNU 标准 C++ 库的一些机制以创建“keyed_set”容器:

// Deriving from the tree is probably not a good idea, but it was easy.

template<typename Key, typename Val, typename Extract,
typename Compare = std::less<Key>, typename Alloc = std::allocator<Val>>
class keyed_set : public std::_Rb_tree<Key, Val, Extract, Compare, Alloc> {
using Base = std::_Rb_tree<Key, Val, Extract, Compare, Alloc>;

public:
template<typename ...Args>
auto insert(Args... args)
->decltype(Base()._M_insert_unique(std::declval<Args>()...)) {
return this->_M_insert_unique(args...);
}

typename Base::iterator insert(typename Base::const_iterator i,
const Val& val) {
return this->_M_insert_unique_(i, val);
}

Val& operator[](const Key& key) {
auto i = this->lower_bound(key);
if (i == this->end() || this->key_comp()(key, Extract()(*i))) {
i = this->_M_insert_unique_(i, Val(key));
}
return *i;
}
};

要完成这项工作,您需要提供一个 key 提取器,如下所示:

template<class T>
struct KeyExtractor;

template<>
struct KeyExtractor<Node> {
const std::string& operator()(const Node& n) { return n.name(); }
};

要让我的 operator[] 版本工作,您需要值类型有一个构造函数,该构造函数将其键类型作为参数。

我遗漏了很多东西(例如删除);但它足以进行简单测试。

从 KeyExtractor 的返回类型中默认 key 类型可能会更好,但这将涉及将模板参数放在不同的顺序中,我已经浪费了太多时间没有注意到 _M_insert_unique 和 _M_insert_unique_ 是拼写不同(大概是为了避免模板实例化问题。)

这是我用来检查以确保其有效的示例; MyKeyedClass 有一个名称、一个字符串 vector 和一个与每个字符串相关联的 double 值。 (没有崇高的目的。)

int main(void) {
keyed_set<std::string, MyKeyedClass, KeyExtractor<MyKeyedClass>> repo;
for (std::string name, val; std::cin >> name >> val; ) {
try {
size_t end;
double d = std::stod(val, &end);
if (end != val.size())
throw std::invalid_argument("trailing letters");
repo[name].increment(d);
} catch (std::invalid_argument(e)) {
repo[name].push(val);
} catch (std::out_of_range(e)) {
std::cerr << "You managed to type an out of range double" << std::endl;
}
}
std::for_each(repo.begin(), repo.end(),
[](MyKeyedClass& c){ std::cout << c << std::endl; });
return 0;
}

关于c++ - 如果我的键是我的值的一部分,我应该使用映射还是集合?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12755395/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com