【问题标题】:C++ unordered_map with custom key resulting in null values带有自定义键的 C++ unordered_map 导致空值
【发布时间】:2014-10-29 06:02:50
【问题描述】:

我正在使用带有 Currency 键的 std::unordered_map 和带有货币价格 double 的值。 Currency 是我制作的自定义类。这是我尝试过的一个版本:

#ifndef CURRENCY_H
#define CURRENCY_H

#include "Nameable.h"
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/functional/hash.hpp>
#include "BigDecimal.h"
#include <iostream>

/**
 * Represents a single currency. Can be used as keys in a map and as a general
 * identifier for determining what unit a value of money is.
 * @param name
 */
class Currency: public Nameable {
public:
    Currency(std::string name) throw(NameAlreadyTakenException);
    Currency(const Currency& orig);
    virtual ~Currency();
    virtual std::string getName();
    virtual void setName(std::string name) throw(NameAlreadyTakenException);
    inline bool operator==(const Currency& key) const {
        return this->id == key.id;
    }

    // A custom hasher that I tried using.
    struct currencyHasher
        {
        std::size_t operator()(const Currency& k) const
        {
            return boost::hash<boost::uuids::uuid>()(k.id);
        }
    };
    boost::uuids::uuid id;
private:

};
// A template specialization for Currency. 
namespace std {
    template <>
    struct hash<Currency> {
        std::size_t operator()(const Currency& k) const {
            cout<< boost::hash<boost::uuids::uuid>()(k.id)<<"\n";
            return boost::hash<boost::uuids::uuid>()(k.id);
        }
    };
}
#endif  /* CURRENCY_H */

这是实现:

#include "Currency.h"

Currency::Currency(std::string name) throw(NameAlreadyTakenException) {
    this->setName(name);
    this->id = boost::uuids::random_generator()();
}

Currency::Currency(const Currency& orig) {

}

Currency::~Currency() {
}

std::string Currency::getName() {
    return this->name;
}

void Currency::setName(std::string name) throw(NameAlreadyTakenException) {
    this->name = name;
}

我尝试通过实现C++ unordered_map using a custom class type as the key 的答案给出的两个建议来使Currency 键兼容。如您所见,我已经覆盖了 operator== 并提供了自定义哈希器以及专门化模板。

尽管如此,键似乎正在失去价值。我的意思是双打,浮点数和整数变成0,字符串变成空字符串。当然,它会导致我用作值的任何其他问题出现其他问题。例如:

Currency dollar("Dollar")
std::unordered_map<Currency,int,Currency::currencyHasher> currenMap;
currenMap[dollar]=1337;
std::cout<<currenMap[dollar]<<"\n";

这个在控制台的输出是0。 使用模板特化也不起作用:

std::unordered_map<Currency,int> currenMap;
currenMap[dollar]=1337;
std::cout<<currenMap[dollar]<<"\n";

也产生一个 0...

CurrencyNameable 的子类这一事实是否会引起问题?我使用 boost::uuid 作为哈希(利用 boost::hash&lt;boost::uuids::uuid&gt; 将 id 转换为 size_t)我不确定我错过了什么,谢谢你的帮助。

【问题讨论】:

  • @sehe 我知道不应该将相等用于浮点数和双精度数。但据我所知,我没有对浮点数和双精度数使用相等性。除非 boost::uuids 用它们来表示。
  • 同意。在看到“货币”之后,我只是用预兆来回应 :) 你已经有了答案。这里自成一体:coliru.stacked-crooked.com/a/50e9f64070a7bafc
  • 与您的问题完全不同,但 FWIW 在运行时将 uuid 分配给每种货币显然是不必要的昂贵和繁琐的做法 - 您是否在进程/系统中使用 uuid?如果没有,std::vector&lt;Currency&gt;(或Current* 或智能指针,如果你真的要派生类)和std::unordered_map&lt;std::string, int&gt; 用于将货币名称映射到矢量索引(或者你可以直接转到Currency*s)性能更好,使用更少的内存。
  • @TonyD 货币名称可以更改,所以我需要一些可以哈希的东西,这样当名称更改时,货币仍然会哈希到正确的值。我的计划是将所有货币及其相关值存储在 unordered_map 中。我不明白如何为货币向量提供 unordered_map 索引如何不那么麻烦,但我可以看到内存优势,似乎 UUID 占用 16 个字节。对 id 使用 int(只有 4 个字节)会是更好的选择吗?

标签: c++ boost unordered-map


【解决方案1】:

问题在于复制构造函数:

Currency::Currency(const Currency& orig) {

}

当您复制 Currency 时,您会得到一个默认构造的 id。当您将 Currency 插入地图时,它会被复制,并且该副本的 id 将与原始的不同。因此:

currenMap[dollar]=1337;

有效地将{Currency(), 1337} 添加到地图中。因此,当您查找具有为dollar 创建的任何 id 的那个时,它将不存在。并不是该值被“归零”……而是您获得了一个默认构造的值。

修复您的复制构造函数应该可以解决问题。

【讨论】:

  • Currency(const Currency &amp;orig) = default; virtual ~Currency() = default; 看起来不错。 RuleOfZero
  • 谢谢我通过将复制构造函数设置为默认值来解决我的问题。 Currency(const Currency&amp; orig) = default;
猜你喜欢
  • 2013-08-30
  • 1970-01-01
  • 1970-01-01
  • 2020-07-03
  • 2013-06-05
相关资源
最近更新 更多