2015-08-11 3 views
3

Рассмотрим следующий фрагмент кода 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() из FooEqual я не могу ссылаться на нестатическая are_foos_equal.

Мой вопрос: возможно ли fooset использовать are_foos_equal как-то? Я знаю, что я мог бы просто ставить are_foos_equal, но код кода, который я дал, просто так, что я мог бы указать свою проблему, которая, к сожалению, произошла в гораздо более крупном проекте, и даже если это означает, что дизайн несколько неправильный, я бы как бы спасти его некоторыми хаками, если это возможно.

EDIT

Я добавил нестатический переменную-член w в Bar подчеркнуть "не-статичность" из are_foos_equal.

+1

Если вы открыты для ** очень ** плохие идеи, если не-статический метод Безразлично действительно ли доступ к любому нестационарному члену, вы можете называть его «nullptr» и молиться. Но почему это не статично в первую очередь? – Quentin

+0

Вы также можете создать временный объект Bar и называть 'are_foos_equal' на нем, это разрешено здесь. – ForEveR

+1

Что здесь делать? Вопрос четко сформулирован, включает соответствующий код и т. Д. – Angew

ответ

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, если вам это нравится.

+0

Ницца, не так или иначе подумал об этом. – Angew

+0

Обеспечение количества ковша не сильно меня беспокоит. Это решение, которое я искал - спасибо! – socumbersome

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); 
    } 
    
  • Invoke Undefined Behavior, invoke are_foos_equal на нулевой указатель и надеемся, что он не сделает ничего плохого. Я сильно препятствовать это:

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

Отрицаю ответственность за третий вариант. – Quentin

+0

@Quentin Не волнуйтесь, это не вдохновило ваш комментарий, я тоже подумал об этом. :-) – Angew

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

Здесь может быть тривиальным, поскольку are_foos_equal может быть статическим, так как он ни использовать ничего не меняет в this

=> первого способа просто объявить are_foos_equal статические.

В противном случае, если вызываемая функция не может быть статичной, поскольку она использует или изменяет ее объект, вам необходимо будет изменить FooEqual, чтобы содержать объект Bar (или указатель или ссылку на него). Поскольку C++ не является java: внутренние классы не имеют скрытого указателя на объект класса.

=> Второй способ добавить ссылку на 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); 
    }