c++ - 接受所有成员函数指针的模板(包括 CV 限定和 ref 限定)
问题描述
我想编写一个模板,它接受指向成员函数的指针(可能是 CV 限定和/或 ref 限定),同时还匹配所有相关类型(返回值的类型、类类型和参数的类型)。
简单版本可能如下所示:
template <typename ReturnValue, typename Class, typename... Arguments>
void foo(ReturnValue(Class::* function_ptr)(Arguments...))
{
// do something with |function_ptr|
}
这对于非 CV-qualified non-ref-qualified member function pointers非常有效,但无法与指向 CV-qualified 和/或 ref-qualified 成员函数的指针匹配。
有没有一种很好的方法可以将它保存在一个模板中并通过以某种方式很好地推导限定符来处理所有情况?
我可以降级为一个接受所有内容的简单模板(可能带有一些SFINAEstd::is_member_function_pointer
)。但我仍然需要在某处提取类型(返回、类和参数),这样实际上不会为我节省任何工作。
解决方案
将成员函数指针传递给foo
并使用帮助特征我们检索类、返回和参数的类型怎么样!
提供类似的特征:
template<typename Class> struct class_traits final {};
template<typename ReType, typename Class, typename... Args>
struct class_traits<ReType(Class::*)(Args...)> final
{
using class_type = Class;
using ret_type = ReType;
};
// traits helpers
template<typename MemFunctionPtr> using class_type = typename class_traits<MemFunctionPtr>::class_type;
template<typename MemFunctionPtr> using ret_type = typename class_traits<MemFunctionPtr>::ret_type;
现在在foo
#include <functional> // std::invoke
template <typename MemFuncType, typename... Args>
void foo(MemFuncType&& func, Args&&... args)
{
// class type can be retrieved!
class_type<MemFuncType> obj{};
// types other than void!!
ret_type<MemFuncType> res = std::invoke(func, obj, std::forward<Args>(args)...);
// do something with |function_ptr|
}
现在是故事的主要部分。以上只会解析成员函数指针的非const
、非volatile
、非noexcept
等版本。正是您所要求的丢失了!
对于其余部分,我们需要提供不同的特征。受这篇文章的启发,我们可以通过宏来完成,这将为我们完成特征的样板复制:
template<typename Class> struct class_traits final {};
#define CREATE_CLASS_TRAITS(...) \
template<typename ReType, typename Class, typename... Args> \
struct class_traits<ReType(Class::*)(Args...)__VA_ARGS__> final \
{ \
using class_type = Class; \
using ret_type = ReType; \
}
CREATE_CLASS_TRAITS();
CREATE_CLASS_TRAITS(const);
CREATE_CLASS_TRAITS(volatile);
CREATE_CLASS_TRAITS(const volatile);
CREATE_CLASS_TRAITS(&);
CREATE_CLASS_TRAITS(const&);
CREATE_CLASS_TRAITS(volatile&);
CREATE_CLASS_TRAITS(const volatile&);
CREATE_CLASS_TRAITS(&&);
CREATE_CLASS_TRAITS(const&&);
CREATE_CLASS_TRAITS(volatile&&);
CREATE_CLASS_TRAITS(const volatile&&);
CREATE_CLASS_TRAITS(noexcept);
CREATE_CLASS_TRAITS(const noexcept);
CREATE_CLASS_TRAITS(volatile noexcept);
CREATE_CLASS_TRAITS(const volatile noexcept);
CREATE_CLASS_TRAITS(&noexcept);
CREATE_CLASS_TRAITS(const& noexcept);
CREATE_CLASS_TRAITS(volatile& noexcept);
CREATE_CLASS_TRAITS(const volatile& noexcept);
CREATE_CLASS_TRAITS(&& noexcept);
CREATE_CLASS_TRAITS(const&& noexcept);
CREATE_CLASS_TRAITS(volatile&& noexcept);
CREATE_CLASS_TRAITS(const volatile&& noexcept);
#undef CREATE_CLASS_TRAITS
// traits helpers
template<typename MemFunctionPtr> using class_type = typename class_traits<MemFunctionPtr>::class_type;
template<typename MemFunctionPtr> using ret_type = typename class_traits<MemFunctionPtr>::ret_type;
现在我们可以:
template <typename MemFuncType, typename... Args>
void foo(MemFuncType&& func, Args&&... args)
{
class_type<MemFuncType> obj{};
using Type = decltype(std::invoke(func, obj, std::forward<Args>(args)...));
static_assert(std::is_same_v<ret_type<MemFuncType>, Type>, "are not same");
std::invoke(func, obj, std::forward<Args>(args)...);
// do something with |function_ptr|
}
class MyClass
{
public:
void func() { std::cout << "MyClass::void func()\n"; }
void func1() const { std::cout << "MyClass::void func1() const\n"; }
void func2() const noexcept{ std::cout << "MyClass::void func2() const noexcept\n"; }
int func3() const { std::cout << "MyClass::func3() const\n"; return {}; }
int func4(int a, double b) const { std::cout << "MyClass::func3() const"<< a << " " << b << "\n"; return {}; }
};
int main()
{
foo(&MyClass::func);
foo(&MyClass::func1);
foo(&MyClass::func2);
foo(&MyClass::func3);
foo(&MyClass::func4, 1, 2.);
return 0;
}
(见在线演示)
推荐阅读
- excel - 将日期从用户表单传输到数据库(包括日期名称)
- android - 找不到adb,但我真的找了好久
- javascript - 不知道为什么我不能让 javascript 显示计算的答案
- android - 设置 HERE 地图样式时如何改善色彩再现?
- r - R ggbiplot 安装错误
- heroku - 我可以在 Heroku 中恢复数据吗
- r - R Shiny - 按下操作按钮后在数据表中保存函数值
- highcharts - highcharts - yAxis 网格线的数量不一致
- azure - 资源 > Azure DevOps yaml 管道中未触发的存储库触发器和未禁用的默认触发器
- c++ - 使用 NVCC 编译/链接时,预处理指令 #\ 无效