[英]Type deduction for a member function pointer
我知道关于 SO 有类似的问题,但那里的用法似乎与我所拥有的不同。 这是我的 MRE:
#include <iostream>
#include <functional>
using namespace std;
void freeFunction() {}
struct Foo { void memberFunction() {} };
template<typename FunctionPtrT>
void foo(FunctionPtrT* f) { f(); }
template<typename InstanceT, typename FunctionPtrT>
void bar(InstanceT&& i, FunctionPtrT f) { std::mem_fn(f)(i); }
template<typename InstanceT, typename FunctionPtrT>
void baz(InstanceT&& i, FunctionPtrT* f) {}
int main() {
foo(&freeFunction); //Ok, obviously
bar(Foo(), &Foo::memberFunction); //Ok, how?!
// Error: candidate template ignored: could not match 'FunctionPtrT *' against 'void (Foo::*)()'
baz(Foo(), &Foo::memberFunction); //why?!
return 0;
}
为什么&Foo::memberFunction
不解析为指针? bar
中的f
是什么类型? 如果它不是指针,我怎么能将它传递给定义为template< class M, class T > /*unspecified*/ mem_fn(MT::* pm) noexcept;
的std::mem_fun
?
我认为回答“在这种情况下x
的类型是什么”之类的问题的最简单方法是放置一个static_assert(std::is_same_v<void, delctype(x)>);
在同样的背景下。 然后编译器会告诉你static_assert
离子失败,因为void
不等于x
的类型,揭示该类型是什么。
&Foo::memberFunction
是指针吗?并且有一些变化。
例如,如果你真的想知道&Foo::memberFunction
是否是一个指针,那么这样问:
static_assert(std::is_pointer_v<decltype(&Foo::memberFunction)>);
编译器会告诉你一些类似的东西
Static_assert failed due to requirement 'std::is_pointer_v<void (Foo::*)()>' [static_assert_requirement_failed]
所以它不能与FunctionPtrT*
匹配。
至于
bar
中的f
是什么类型?
如果你把
static_assert(std::is_same_v<void, decltype(f)>);
在bar
里面,编译器会告诉你
Static_assert failed due to requirement 'std::is_same_v<void, void (Foo::*)()>'
意味着f
的类型是void(Foo::*)()
。
&Foo::memberFunction
的类型如何来自自由函数指针?将此与您定义时得到的错误进行比较
void f();
并断言
static_assert(std::is_same_v<void, decltype(f)>);
编译器会告诉你
Static_assert failed due to requirement 'std::is_same_v<void, void (*)()>'
所以关键是void(*)()
是一个指向从void
到void
的自由函数的指针,它不同于void (Foo::*)()
,它是一个从void
到void
的Foo
函数的指针成员。
如果你希望你的模板只匹配成员函数,可以去
template<typename InstanceT, typename R, typename C>
void baz(InstanceT&& i, R C::* f) { }
或者,如果您希望它匹配Foo
的任何成员函数,您可以清楚地更改模板参数的顺序,以便您可以手动提供C
template<typename C, typename R, typename InstanceT>
void baz(InstanceT&& i, R C::* f) { }
它允许您调用baz<Foo>(/* args */);
,或者您可以简单地硬编码Foo
代替C
:
template<typename R, typename InstanceT>
void baz(InstanceT&& i, R Foo::* f) { }
如您所见,我们已经有一个占位符R
,用于返回类型。
如果我们还想为参数设置一个占位符,我们可以这样做:
template<typename R, typename C, typename ...Args>
void baz(R (C::*f)(Args...));
这将与前面的示例匹配相同的功能。 我展示这个只是为了让你看到,如果你想输入参数,你必须在C::*f
周围加上括号,或者只是C::*
,如果你想省略名称(缺少括号是您问题下评论中的错字)。 因此,如果您想将匹配限制为特定签名,比如说void()
,您可以这样做:
template<typename C>
void baz(void (C::*f)());
bar中的f是什么类型?
bar
中f
的类型是void (Foo::*)()
,即指向类Foo
的成员函数的指针,该函数没有参数并且返回类型为void
。
原因baz(Foo(), &Foo::memberFunction);
失败在于&Foo::memberFunction
是一个指向成员函数的指针,它不同于指向某个类型的普通指针。
事实上, 成员函数指针实际上并不是指针。 因此void (Foo::*)()
类型的参数不能传递给参数FunctionPtrT* f
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.