Оболочка функции-члена с шаблоном с одним аргументом?
Я сделал шаблонную функцию, которая принимает функцию-член в качестве параметра.
Однако, поскольку класс должен быть объявлен до того, как его можно будет использовать как часть параметра функции-члена, я должен сделать его отдельным параметром:
template<typename C, void (C::*Method)(void)>
function<void(C*)> methodWrap()
{
}
Это означает, что при явном создании экземпляра шаблона (я хочу, чтобы эти обертки генерировались во время компиляции, не передавая указатель члена в качестве аргумента), я должен набрать его дважды, когда я его использую:
function<void(C*)> someFunc = wrapMethod<SomeClass, &SomeClass::someMethod>();
Почему я не могу просто написать что-то вроде этого:
template<void (C::*Method)(void)>
function<void(C*)> methodWrap()
{
}
и позволить ему захватить тип C и указатель на его функцию-член без необходимости вводить SomeClass дважды?
Или почему я не могу обернуть его во внешний шаблон, который объявляет C как "свободную переменную", а затем имеет внутренний аргумент шаблона, который выполняет дедукцию
template<typename C>
template<void (C::*Method)(void)>
function<void(C*)> methodWrap()
{
}
2 ответа
Если вы можете жить с наличием нормального параметра функции для указателя на функцию-член вместо того, чтобы сделать его параметром шаблона, вы можете сделать
#include <functional>
template<typename R, typename C, typename... Args>
struct MemberFunctionPointer
{
typedef R Return;
typedef C Class;
};
template<typename R, typename C>
constexpr auto inferMemberFunctionPointer(R (C::*method)())
{
return MemberFunctionPointer<R,C>{};
}
template<typename T>
constexpr auto methodWrap(T m)
{
typedef typename decltype(inferMemberFunctionPointer(m))::Class Class;
typedef typename decltype(inferMemberFunctionPointer(m))::Return Return;
return std::function<Return (Class*)>();
}
struct B {};
struct A
{
B f();
};
void foo()
{
auto const m = methodWrap( &A::f );
}
std::function
это не constexpr
типа, поэтому мы не можем использовать methodWrap для инициализации constexpr
переменная. Вы можете обойти это, создав свой собственный простой constexpr
обертка функции-члена. Я также добавил static_assert
чтобы получить лучшие сообщения об ошибках.
#include <functional>
template<typename R, typename C, typename... Args>
struct MemberFunctionPointer
{
typedef R Return;
typedef C Class;
};
template<typename R, typename C>
constexpr auto inferMemberFunctionPointer(R (C::*method)() const)
{
return MemberFunctionPointer<R,C>{};
}
template<typename R, typename C>
struct MemberFunction
{
constexpr explicit MemberFunction(R (C::*g)() const): f(g) {}
constexpr R operator()(C const* obj) const
{
return (obj->*f)();
}
R (C::*f)() const;
};
template<typename T>
constexpr auto methodWrap(T m)
{
static_assert( std::is_member_function_pointer<T>::value,
"Member function pointer expected!");
typedef typename decltype(inferMemberFunctionPointer(m))::Class Class;
typedef typename decltype(inferMemberFunctionPointer(m))::Return Return;
return MemberFunction<Return, Class>{ MemberFunctionPointer<Return,Class>{} };
}
struct B {};
struct A
{
constexpr B f() const;
};
void foo()
{
auto constexpr m = methodWrap( &A::f );
auto constexpr a = A{};
m( &a );
auto b = A{};
m( &b );
}
Почему я не могу просто написать что-то вроде этого:
template<void (C::*Method)(void)> function<void(C*)> methodWrap()
Потому что нет способа определить C
Тип здесь. Отсюда необходимость typename C
,
Или почему я не могу обернуть его во внешний шаблон, который объявляет C как "свободную переменную", а затем имеет внутренний аргумент шаблона, который выполняет дедукцию
Я не уверен, как это поможет вашему затруднительному положению. Вам все еще нужен способ указать тип объекта в списке аргументов вашего шаблона.
Вы могли бы сделать что-то вроде этого:
template<typename C>
std::function<void(C&)> wrap(void(C::*mf)())
{
return std::function<void(C&)>(mf);
}
Использование: wrap(&Obj::memfun);
И, конечно же, чтобы фактически вызывать нестатическую функцию-член через указатель, вам понадобится экземпляр объекта.