需要调用没有对象的非静态方法

Need to invoke non-static method without object

考虑以下 C++ 代码:

class Foo {
public:
    int a;
};

class Bar {
public:
    int w = 1;
    bool are_foos_equal(Foo* f1, Foo* f2) { return f1->a * w == f2->a * w; }

    struct FooHash { size_t operator () (Foo* f) const { return f->a; } };
    struct FooEqual { 
        bool operator () (Foo* f1, Foo* f2) const { 
            return are_foos_equal(f1, f2); 
        } 
    };

    std::unordered_set<Foo*, FooHash, FooEqual> fooset;
};

现在,这无法编译,因为在 FooEqualoperator () 中我无法调用非静态 are_foos_equal.

我的问题是:fooset 是否有可能以某种方式使用 are_foos_equal?我知道我可以将 are_foos_equal 设为静态,但我给出的示例代码只是为了说明我的问题,不幸的是,这个问题发生在一个更大的项目中,即使这意味着设计有些错误,如果可能的话,我想通过一些技巧来拯救它。

编辑

我在Bar中添加了一个非静态成员变量w来强调are_foos_equal的"non-staticness"。

正确的选择肯定是使 are_foos_equal 静态化。我强烈建议这样做而不是 hack。项目越大,它就需要越干净,这样它才不会陷入无法维护的混乱局面。

但如果这真的不是一个选项,我看到了一些其他的可能性:

  • FooEqual:

    中动态创建一个 Bar 对象
    return Bar().are_foos_equal(f1, f2);
    
  • 为此目的 FooEqual 存储静态 Bar 对象:

    bool operator() (Foo* f1, Foo* f2) const {
      static Bar bar;
      return bar.are_foos_equal(f1, f2);
    }
    
  • 调用未定义的行为,在空指针上调用are_foos_equal并希望它不会做任何坏事。我强烈反对这个:

    return static_cast<Bar*>(nullptr)->are_foos_equal(f1, f2);
    

您可以在 FooEqual 中维护对父 Bar 对象的引用:

Bar() : fooset{10, FooHash{}, FooEqual{*this}}
{}

struct FooEqual {
    Bar& parent;
    bool operator () (Foo* f1, Foo* f2) const { 
        return parent.are_foos_equal(f1, f2); 
    } 
};

由于在 std::unordered_set 中声明构造函数的方式,您需要提供存储桶计数,这有点遗憾。如果您愿意,可以从默认构造的 std::unordered_set 中获取默认值。

are_foos_equal() 移到 class 之外,使其成为自由函数。它应该是 Bar 的成员没有意义。示例:

class Foo {
public:
    int a;
};

bool are_foos_equal(Foo* f1, Foo* f2)
{return f1->a == f2->a;}

class Bar {
public:
    struct FooHash { size_t operator () (Foo* f) const { return f->a; } };
    struct FooEqual {
        bool operator () (Foo* f1, Foo* f2) const {
            return are_foos_equal(f1, f2);
        }
    };
    std::unordered_set<Foo*, FooHash, FooEqual> fooset;
};

这里可能是微不足道的,因为 are_foos_equal 可能是静态的,因为它既不使用也不改变 this

中的任何内容

=> 第一种方式只是声明 are_foos_equal 静态。

或者,如果被调用的函数不能是静态的,因为它使用或更改了它的对象,您将不得不更改 FooEqual 以包含一个 Bar 对象(或指向它的指针或引用) .因为 C++ 不是 java: 内部 classes 没有隐藏的指针指向包含 class.

的对象

=> 第二种方式在 FooEqual 中添加对 Bar 的引用并在构建时设置它:

struct FooEqual {
    const Bar &bar;
    FooEqual(const Bar& bar): bar(bar) {};
    bool operator () (Foo* f1, Foo* f2) const { 
        return bar.are_foos_equal(f1, f2); 
    }