考虑代码:
class Character
{
void kill();
void send_to_wall();
}
template <typename T>
void GeorgeFunc(T fp)
{
??? obj;
(obj.*fp)();
}
int main()
{
GeorgeFunc(&Character::kill);
}
所以我的问题是:我怎样才能得到
???
?看来编译器在模板实例化期间肯定知道这个类型是什么(Character
),但我不知道如何获取它。我当前的解决方法是更改为:void GeorgeFunc(void (T::*fp)())
,但简单地从成员函数指针获取类型会更干净。 decltype(fp)
将返回 void(Character::*)()
,decltype(fp())
将返回 void
。有什么办法可以得到Character
吗?
是的,只需使用一个特质来确定这一点。
template <typename> struct member_function_traits;
template <typename Return, typename Object, typename... Args>
struct member_function_traits<Return (Object::*)(Args...)>
{
typedef Return return_type;
typedef Object instance_type;
typedef Object & instance_reference;
// Can mess with Args... if you need to, for example:
static constexpr size_t argument_count = sizeof...(Args);
};
// If you intend to support const member functions you need another specialization.
template <typename Return, typename Object, typename... Args>
struct member_function_traits<Return (Object::*)(Args...) const>
{
typedef Return return_type;
typedef Object instance_type;
typedef Object const & instance_reference;
// Can mess with Args... if you need to, for example:
static constexpr size_t argument_count = sizeof...(Args);
};
现在您的声明是:
typename member_function_traits<T>::instance_type obj;
但是,我认为,由于您需要一个成员函数指针(由于行
(obj.*fp)()
1,其他类型将无法实例化),因此您的函数应该直接采用成员函数指针而不是完全通用的类型.
所以这个定义不仅有效,而且我认为它是首选——当有人使用除成员函数指针之外的东西时,错误消息会更清晰,因为参数类型将不兼容:
template <typename Return, typename Object>
void GeorgeFunc(Return (Object::*fp)())
{
Object obj;
(obj.*fp)();
}
请注意,这确实允许传递返回任何类型的成员函数指针。由于我们并不真正使用返回值,因此我们不关心它是什么。没有理由强制它是“解决方法”中的
void
。
使用此方法的唯一缺点是,如果您还打算接受指向声明为
const
的成员函数的指针,则需要两个重载。完全通用的实现没有这个限制。 (我一直希望指向 const
成员函数的指针能够隐式转换为指向非 const
成员函数的指针,但目前 C++ 不允许这样做。)
1 这并非100%正确。如果您像现在一样使用完全泛型类型,那么理论上调用者可以传递成员data指针而不是成员function指针。
obj.*fp
将评估为对数据成员的引用,然后您将对其调用 operator()()
。只要数据成员的类型实现了这个运算符,那么模板函数GeorgeFunc
就可以被实例化。
在 C++17 中,可以使用演绎指南来确定类类型和常量性,而无需太多样板:
template<bool Const = false, typename R = void, typename C = void, typename... Args> struct MemberFunctionPointerInfoDetail
{
using ClassType = C;
using ReturnType = R;
using Arguments = std::tuple<Args...>;
static constexpr bool isConst = Const;
//explicit MemberFunctionPointerInfoDetail(auto){} <-c++20, thx
杜布卡
模板显式 MemberFunctionPointerInfoDetail(T){}
};
template
template<typename mfp> using MemberFunctionPointerInfo = decltype(MemberFunctionPointerInfoDetail((mfp*){}));
您现在可以根据需要使用
MemberFunctionPointerInfo<MFPType>::ClassType
。
与 ResultType、Arguments(作为元组)和 isConst 相同。
void-default 参数有助于防止推导失败时出现编译器错误。基本上,
MemberFunctionPointerInfo<int>
不会导致编译错误,但ClassType会变成void。如果需要描述性编译器错误,可以 static_assert 这个条件。