Частичная специализация шаблона функтора, вызывающего метод-член переданных ему объектов.

У меня есть следующий функтор и его частичная специализация

template    <class _T, typename _Return = void, typename _Arg = void>
struct  Caller
{
    typedef _Return(_T::*Method)(_Arg);

    Caller(Method pm, _Arg a)
    :   _pMethod(pm),
        _arg(a)
    {}

    _Return operator()(_T& obj)
    {
        return (obj.*_pMethod)(_arg);
    }

    Method  _pMethod;
    _Arg    _arg;
};

template    <class _T, typename _Return>
struct  Caller<_T, _Return, void>
{
    typedef _Return(_T::*Method)();

    Caller(Method pm)
    :   _pMethod(pm)
    {}

    _Return operator()(_T& obj)
    {
        return (obj.*_pMethod)();
    }

    Method  _pMethod;
};

Я пытаюсь использовать его следующим образом:

struct Foo
{
    void Bar() const
    {
         void(0);
    }
};

// ...

std::list<Foo> foos;
const std::list<Foo> &rFoos(foos);
std::for_each(rFoos.begin(), rFoos.end(), Caller<const Foo>(&Foo::Bar));

Я получаю это для последней строки кода (среда IDE принимает вызов от вызывающего абонента):

ошибка C2440: '': невозможно преобразовать из 'void (__thiscall Foo::*)(void) const' в 'Caller‹_T>' 1> с 1> [ 1> _T=const Foo 1> ] 1> Ни один конструктор не может взять исходный тип, или разрешение перегрузки конструктора было неоднозначным

Этот код будет работать в среде g++. (Если бы я Caller<Foo>(&Foo::Bar) g++ жаловался, что имеет смысл, так как функция будет вызываться только для объекта const).

Я пробовал разные вещи, в том числе добавлял в функтор разновидности operator()(const _T& obj)/operator()(const _T& obj) const, но безрезультатно.

Это будет принято компилятором:

struct Foo
{
    void Bar()
    {
         void(0);
    }
};

// ...

std::list<Foo> foos;
const std::list<Foo> &rFoos(foos);
std::for_each(rFoos.begin(), rFoos.end(), Caller<Foo>(&Foo::Bar));

Что я делаю неправильно? Как заставить шаблон функтора работать с константными функциями-членами в Visual C++?


person zyndor    schedule 15.07.2011    source источник


Ответы (2)


Я думаю, что константность _T в Caller не отражается в константности this Method автоматически в MSVC (во всяком случае, мне кажется, что поведение GCC, о котором вы упомянули, странно).
Если вы хотите отразить константность _T в константности this, как насчет подготовки вспомогательного класса Select_Type, как показано ниже, и выбора правильной подписи в соответствии с константностью T_?

template <class C, class T, class Const_T>
struct Select_Type { typedef T type; };

template <class C, class T, class Const_T>
struct Select_Type<C const, T, Const_T> { typedef Const_T type; };

template    <class _T, typename _Return>
struct  Caller<_T, _Return, void>
{
    typedef typename Select_Type<
        _T, _Return(_T::*)(), _Return(_T::*)()const >::type Method;
    ....
person Ise Wisteria    schedule 15.07.2011
comment
Спасибо, это именно то, что я искал! - person zyndor; 16.07.2011

Ваши функции operator() должны быть константными (они не мутируют сам функтор, но я не думаю, что вам нужен новый набор функций).

Также обратите внимание, что все ваши типы, начинающиеся с подчеркивания и заглавной буквы, зарезервированы стандартом для реализации.

person Mark B    schedule 15.07.2011
comment
Спасибо за замечание о стандартах именования — это относится только к именам типов? - person zyndor; 15.07.2011
comment
...и спасибо за предложение; сейчас попробовал - не помогло. Теперь я изменил вопрос, чтобы лучше отразить исходную проблему. (Протестировано предложение с обоими фрагментами.) - person zyndor; 15.07.2011