无法在具有自定义运算符 ==() 的 c++ 无序集中找到用户定义的类型

Posted

技术标签:

【中文标题】无法在具有自定义运算符 ==() 的 c++ 无序集中找到用户定义的类型【英文标题】:Unable to find a user-defined type in a c++ unordered set with custom operator==() 【发布时间】:2020-02-26 20:39:05 【问题描述】:

问题陈述:遍历对象数组并检查对象是否存在于 unordered_set 中。

目标:我可以在一个容器中拥有数千个对象,以检查它们是否存在于另一个容器中的数百万个对象中。我选择 unordered_set 是因为它不断发现复杂性和迭代向量。我是新手,如果您有其他方法,我将不胜感激。

问题: unordered_set find 没有按预期工作,或者我的概念有误!

主要:

int main() 
  std::vector<std::unique_ptr<Block>> vertices;

  vertices.push_back(std::make_unique<Block>("mod1", "work"));
  vertices.push_back(std::make_unique<Block>("mod2", "work"));
  vertices.push_back(std::make_unique<Block>("mod3", "work"));

  std::unordered_set<std::unique_ptr<Block>> undefs;

  undefs.insert(std::make_unique<Block>("mod1", "work"));
  undefs.insert(std::make_unique<Block>("mod2", "work"));

  for(auto& vertex : vertices) 
    auto search = undefs.find(vertex);
    if(search != undefs.end())
      std::cout << "Block: " << vertex->getName() << "\n";
    
  

块类重载:

bool Block::operator==(std::unique_ptr<Block>& block) const 
  return block->getName() == mName;

预期输出:

mod1

mod2

屏蔽:

#pragma once

#include <string>
#include <memory>

using std::string;

class Block 
  private:
    string mName;
    string mLib;
  public:
    Block(string const& name, string const& lib);
    string getName() const;
    string getLib() const;
    bool operator==(std::unique_ptr<Block>& block) const;
;

【问题讨论】:

有什么问题?你得到了什么输出?你期待什么? 请告诉我们Block 是什么,以及编译器给你什么样的错误信息。 常量作为参数?如bool Block::operator==(const std::unique_ptr&lt;Block&gt;&amp; block) const 我没有收到任何错误信息并且代码编译正确。但是当我跑的时候,我没有得到任何输出。 你阻止类重载operator== 没关系,因为vector和unordered_set中的对象都是智能指针。代码是比较智能指针,而不是 Block 对象。 【参考方案1】:

您正在尝试比较指针,而不是值。 您需要为Block 类指定散列函数。

例如,如果您想使用 mName 作为键,则代码如下:

class Block 
private:
    string mName;
    string mLib;
public:
    Block(string const& name, string const& lib)
    
        mName = name;
        mLib = lib;
    
    string getName() const 
        return mName;
    ;
    string getLib() const 
        return mLib;
    
    bool operator==(const Block & block) const;
;

template<> struct std::hash<Block> 
    std::size_t operator()(const Block & block) const noexcept 
        return std::hash<std::string>(block.getName());
    
;

bool Block::operator==(const Block & block) const 
    return block.getName() == mName;


int main() 
    std::vector<Block> vertices;

    vertices.emplace_back(Block("mod1", "work"));
    vertices.emplace_back(Block("mod2", "work"));
    vertices.emplace_back(Block("mod3", "work"));

    std::unordered_set<Block> undefs;
    undefs.emplace(Block("mod1", "work"));
    undefs.emplace(Block("mod2", "work"));

    for (auto& vertex : vertices) 
        auto search = undefs.find(vertex);
        if (search != undefs.end()) 
            std::cout << "Block: " << vertex.getName() << "\n";
        
    

【讨论】:

谢谢。我明白了这个问题。与存储指向它们的指针相比,我唯一关心的是直接存储对象。正如我在目标中解释的那样,我在这里处理大量对象,性能是我的首要任务。 @Sourab Sharma,如果性能很重要,我建议您在内部使用散列:您可以计算 mName 的散列(例如)并将其用作对象的键。如果您将主要在内部使用这些哈希而不是名称进行操作,它将比每次哈希字符串快得多。这有时被称为“字符串内联”,但有时它意味着前缀树,而不是散列。在大多数情况下,散列是最佳的,尤其是在没有冲突的情况下 哎呀,我的意思是“字符串实习”,而不是内联 :) 我还强烈建议您阅读《游戏引擎架构》这本书,它在性能方面描述了许多此类优化。 我想我现在有足够的东西可以尝试和使用。再次感谢。我也会看看这本书。【参考方案2】:

unordered_set 需要散列函数和比较函数。您正在使用std::unique_ptr 的现有散列和比较函数,这绝对不是您想要的。

我不建议尝试更改 std::unique_ptr&lt;Block&gt; 的行为,因为这会导致其他需要正常语义指针的代码混淆。相反,为Block 添加普通的散列和比较函数,并将自定义的传递给unordered_set 的构造函数。

【讨论】:

谢谢。我明白了这个问题。对目标部分有何评论? 我会避免使用std::vector,除非您可以在任何插入之前调整它的大小(使用reserve)。否则,请使用std::list 好的。谢谢大卫。【参考方案3】:

问题在于您正在尝试比较不同的指针! 我不知道使用 unique_ptr 背后的原因,但这样做实际上是在尝试比较身份,而不是您想要的状态。

所以你可以明白我的意思,假设第一个 Block 对象在你的记忆中的位置 100。那将是它的身份。所以我们有object1,它的状态是“mod1,work”,它的身份是100。然后我们有object2,它的身份是150,但它的状态和object1一样,“mod1,work”。

向量和 unordered_set 中的所有内容都是指针,因此您有内存位置。将它们插入向量中时,您插入了位置 100。但在 unordered_set 中插入了 150。它们具有相同的状态,但 find 方法正在寻找内存位置。

希望我的回答对您有所帮助。如果您在这里发现任何错误或有不同的想法,请告诉我。祝你好运! :)

【讨论】:

我理解你的意思(非常善解人意地解释,谢谢)但是如果我必须在堆栈上创建数百万个对象并将它们存储在一个容器中,与存储指向的指针相比,这不是一个问题他们? @SourabSharma 建立在std::list 之上的堆栈已经存储了指向对象的指针。只是不要建立在 std::vector 之上。 @SourabSharma 你在堆栈上是什么意思?如果您使用std::vector/ 任何其他容器数据在内部使用new 存储在堆上(准确地说,这是由分配器定义的)。您不太可能希望在堆栈上存储数百万个对象。通常,堆栈不超过 10mb。 @espkk 好吧,我不知道。

以上是关于无法在具有自定义运算符 ==() 的 c++ 无序集中找到用户定义的类型的主要内容,如果未能解决你的问题,请参考以下文章

C++ 将自定义类型的转换运算符重载为 std::string

您可以在 C++ 中制作自定义运算符吗?

C++ 介绍——自定义数据类型

C++学习摘要之七:运算符重载

我们如何使用 '=' 运算符为 C++ 中的自定义数据类型赋值?

C++ 使用具有无序映射的模板类型