繁体   English   中英

成员函数指针的类型推导

[英]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(*)()是一个指向从voidvoid的自由函数的指针,它不同于void (Foo::*)() ,它是一个从voidvoidFoo函数的指针成员

如何限制模板仅用于成员函数?

如果你希望你的模板只匹配成员函数,可以去

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是什么类型?

barf的类型是void (Foo::*)() ,即指向类Foo的成员函数的指针,该函数没有参数并且返回类型为void


原因baz(Foo(), &Foo::memberFunction); 失败在于&Foo::memberFunction是一个指向成员函数的指针,它不同于指向某个类型的普通指针

事实上, 成员函数指针实际上并不是指针 因此void (Foo::*)()类型的参数不能传递给参数FunctionPtrT* f

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM