需要在没有对象

时间:2015-08-11 12:18:18

标签: c++ templates object static circular-dependency

考虑下面的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;
};

现在,这不会编译,因为在operator ()的{​​{1}}我无法调用非静态FooEqual

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

修改

我向are_foos_equal添加了一个非静态成员变量w,以强调Bar的“非静态”。

4 个答案:

答案 0 :(得分:3)

are_foos_equal()移到课堂外,使其成为免费功能。它应该是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;
};

答案 1 :(得分:2)

正确的选择肯定是使are_foos_equal静态。我强烈建议这样做而不是黑客攻击。项目越大,它就越需要清洁,以免它变成一个无法维护的混乱。

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

  • Bar内动态创建FooEqual对象:

    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);
    

答案 2 :(得分:2)

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

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获取默认值。

答案 3 :(得分:1)

这里可能很简单,因为are_foos_equal可能是静态的,因为它既不会改变this中的任何内容

=&GT;第一种方法只是声明are_foos_equal静态。

或者,如果被调用的函数不能是静态的,因为它使用或更改了它的对象,则必须更改FooEqual以包含Bar对象(或指向它的指针或引用)。因为C ++不是java:内部类没有隐藏指向封闭类的对象的指针。

=&GT;第二种方法是在Bar中添加对FooEqual的引用,并在构建时设置它:

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); 
    }