Вывод аргумента шаблона для указателей функций-членов

Известно, что аргументы шаблона могут быть указателями на функции-члены.

Поэтому я могу написать:

struct Bar
{
    int fun(float x);
};

template <int (Bar::*FUN)(float)>
struct Foo
{ /*...*/ };

typedef Foo<&Bar::fun> FooBar;

Но что, если я хочу, чтобы тип Bar был аргументом шаблона:

template <typename B, int (B::*FUN)(float)>
struct Foo
{ /*...*/ };

typedef Foo<Bar, &Bar::fun> FooBar;

Теперь, когда я его использую, мне нужно дважды писать Bar!

Мой вопрос: есть ли способ заставить компилятор автоматически выводить тип класса?

Цель состоит в том, чтобы это просто работало:

typedef Foo<&Bar::fun> FooBar;
typedef Foo<&Moo::fun> FooMoo;

Ответы

Ответ 1

Вы, вероятно, должны просто написать имя класса там. Однако, если вы действительно хотите избежать этого, вы можете использовать злую магию макросов. Простая версия более опасна:

#define TT(X) decltype(X), X

template<typename T,T t>
struct Foo
{ /* ... */ };

struct Bar {
    int fun(float) {}
};

int main() {
    Foo<TT(&Bar::fun)> f;
}

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

Чтобы сделать его более безопасным, вам понадобится метафейс, который сообщает вам имя класса:

template<typename T> struct member_ptr_traits;

template<typename Class,typename Ret,typename... Args>
struct member_ptr_traits<Ret (Class::*)(Args...)>
{
    typedef Class class_type;
    typedef Ret return_type;
};

#define TT(X) member_ptr_traits<decltype(X)>::class_type , X

template<typename T,int (T::*FUN)(float)>
struct Foo
{ /* ... */ };

struct Bar {
    int fun(float) {}
};

int main() {
    Foo<TT(&Bar::fun)> f;
}

Также оба из них используют С++ 11, поэтому они не будут работать со старыми компиляторами. Эта простая версия может быть переписана для использования старых typeof или подобных расширений компилятора. Переписывание более безопасной версии требует моделирования вариативных шаблонов.

Ответ 2

Простой ответ: нет.

Проблема заключается в том, что для работы typedef Foo<&Bar::fun> FooBar; у шаблона должен быть один аргумент non-type, но тип этого аргумента будет неизвестен при объявлении шаблона, что недопустимо. С другой стороны, вывод типа никогда не применяется к аргументам шаблона (только к аргументам для функциональных шаблонов, но это аргументы функции, а не шаблон).