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

Създавам поредица от предикати, които дублират много код и така се променят в единичен шаблонен функционален клас, базиран на 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, ако има такъв (или void, ако няма). Следователно го комбинирате така:

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