Создание класса предиката шаблона, требующего указателя на функцию метода, и вытекающие из этого ошибки компилятора

Я создаю серию предикатов, которые дублируют много кода, и поэтому они превращаются в один класс функции шаблона на основе std::unary_function. Идея состоит в том, что интерфейс моего класса требует, чтобы были определены такие методы, как Element_t Element() и std::string Name(), поэтому аргументами шаблона предиката являются тип объекта и тип значения, с которыми будет производиться сравнение следующим образом:

// generic predicate for comparing an attribute of object pointers to a specified test value
template <class U, typename R>
class mem_fun_eq : public std::unary_function <U*, bool> {
  private:
    typedef R (U::*fn_t)();
    fn_t fn; 
    R val;
  public:
    explicit mem_fun_eq (fn_t f, R& r) : fn(f), val(r) { } 
    bool operator() (U * u) const {
      return (u->*fn)() == val;
    }   
}; 

Таким образом, если у меня есть:

class Atom {
  public:
    const Element_t& Element() const { return _element; }
    const std::string& Name() const { return _name; }
};

Я хотел бы выполнить поиск в контейнере Atoms и проверить равенство Name или Element, используя мой предикат шаблона следующим образом:

typedef std::string (Atom::*fn)() const;

Atom_it it = std::find_if( _atoms.begin(), _atoms.end(), mem_fun_eq <Atom, std::string> ((fn)&Atom::Name, atomname));

но при его компиляции в строке std::find_if возвращается следующая ошибка:

error: address of overloaded function with no contextual type information

Также, пытаясь сформировать тот же предикат для проверки Element() как такового:

typedef Atom::Element_t& (Atom::*fn)() const;

Atom_it it = std::find_if(_atoms.begin(), _atoms.end(), mem_fun_eq <Atom, Atom::Element_t> ((fn)&Atom::Element, elmt);

создает другую ошибку!

error: no matching function for call to ‘mem_fun_eq<Atom, Atom::Element_t>::mem_fun_eq(Atom::Element_t& (Atom::*)()const, const Atom::Element_t&)’
note: candidates are: mem_fun_eq<U, R>::mem_fun_eq(R (U::*)(), R&) [with U = Atom, R = Atom::Element_t]
note:                 mem_fun_eq<Atom, Atom::Element_t>::mem_fun_eq(const mem_fun_eq<Atom, Atom::Element_t>&)

Во-первых, изобретаю ли я велосипед с помощью этого предиката? Есть ли в STL что-то, что я пропустил, что делает ту же работу в одном классе? Я всегда могу разбить предикат на несколько более конкретных, но я стараюсь этого избежать.

Во-вторых, можете ли вы мне помочь с ошибками компилятора?


person Shamster    schedule 26.07.2010    source источник
comment
Я ответил на аналогичный вопрос, в котором целью было сравнение атрибутов членов здесь   -  person David Rodríguez - dribeas    schedule 27.07.2010


Ответы (2)


Я не знаю простого способа сделать это с помощью битов, поставляемых с STL. Вероятно, есть какой-нибудь умный способ ускорения с использованием адаптеров итераторов или boost :: lambda, но лично я бы не пошел по этому пути.

Очевидно, что лямбда-выражения C ++ 0x упростят все это.

Ваша проблема заключается в попытке вызвать такую ​​функцию:

const std::string&(Atom::*)()

в такую ​​функцию:

std::string (Atom::*)()

Если вы замените свой typedef R (U::*fn_t)(); на typedef const R& (U::*fn_t)() const;, он должен работать.

Следующее позволяет избежать этой проблемы, а также обеспечивает вывод типа, так что вы можете просто написать mem_fun_eq(&Atom::Name, atomname). Он компилируется для меня, хотя я его не тестировал.

template<typename U, typename R, typename S>
class mem_fun_eq_t : public std::unary_function<U const*, bool>
{
private:
    R (U::*fn_)() const;
    S val_;
public:
    mem_fun_eq_t(R (U::*fn )() const, S val) : fn_(fn), val_(val){}
    bool operator()(U * u)
    {
        return (u->*fn_)() == val_;
    }
};

template<typename U, typename R, typename S>
mem_fun_eq_t<U, R, S> mem_fun_eq(R (U::*fn)() const, S val)
{
    return mem_fun_eq_t<U, R, S>(fn, val);
}
person Richard Wolf    schedule 26.07.2010

Вы думали о том, чтобы попытаться смешать объект mem_fun_ref или mem_fun вместо вызова функции-члена?

По сути, вы вызываете mem_fun для создания объекта, который принимает два аргумента T* и аргумент шаблона функции A, если он есть (или недействителен, если его нет). Следовательно, вы комбинируете это так:

template<typename MemFunc, typename CompareType, typename T>
struct MyPredicate{
    MyPredicate(MemFunc _functionObj, CompareType _value) 
    : m_Value(_value),
    m_Function(_functionObj){}

    bool operator()(const T &_input){
         return m_Value == m_Function(_input);
    }

 private:
    MemFunc m_Function;
    CompareType m_Value;
 };

Редактировать:

Хорошо, это не полностью работает, так почему бы не иметь:

struct NamePred: binary_function<Atom*,string,bool>{
    bool operator()(Atom *_obj, string _val){
        return _obj->Name() == _val;
    };
};

затем используйте bind2nd

find_if( atoms.begin(), atoms.end(), bind2nd( NamePred, "yo" ) );
person wheaties    schedule 27.07.2010
comment
Да, просто указав тип функции параметром шаблона, вы избавитесь от большинства проблем. Вы должны это сделать. - person Richard Wolf; 27.07.2010
comment
Можно ли вывести тип MemFunc? - person Shamster; 27.07.2010
comment
@Shamster: хороший момент. Для достижения требуемого вывода типа потребуется функция-оболочка, аналогичная функциям-оболочке mem_fun и mem_fun_ref. - person Richard Wolf; 27.07.2010
comment
Я на самом деле пытаюсь скомпилировать его с VS2008, но это именно та проблема, с которой я сталкиваюсь. Возникли проблемы с определением типа возвращаемого значения mem_fun. Блин, думал, это поможет. Я буду продолжать работать над этим. - person wheaties; 27.07.2010