为什么 std::function 存储对成员函数的调用可以有两种不同的可调用类型

why std::function that store a call to member function can have two different callable type

struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_+i << '\n'; }
    int num_;
};

int main ()
{
    function<void(const Foo*, int)> func = &Foo::print_add;
    function<void(const Foo&, int)> func2 = &Foo::print_add;
    Foo f(1), f2(2);
    func(&f, 2);
    func2(f2, 3);
    return 0;
}

为什么funcfunc2都可以正确调用成员函数Foo::print_add?它们具有不同的可调用类型作为模板参数。据我所知,“this”指针作为隐藏参数传递给所有非静态成员函数调用,func2 的可调用类型接收 const Foo& 作为与 this 指针类型不匹配的参数。为什么上面的代码不会产生编译器错误?

why func and func2 both can call member function Foo::print_add correctly?

他们可以,因为标准说他们可以。

From what I know, ‘this’ pointer is passed as a hidden argument

从语言的角度来看,没有“隐藏指针传递”。一个函数被称为“在”一个对象上,this 只是一个关键字,它产生指向该对象的指针。考虑到符号 not_a_pointer.member_function(other_arguments) 似乎没有指针在语法上被传递。

在语言之下,从实现的角度来看(即传递“隐藏”的东西),引用和指针生成的代码没有区别。它们只是内存地址。


顺便说一句:如果 this 生成一个引用而不是一个指针会方便得多。据我了解,成员函数和 this 是在添加引用之前添加到语言中的,因此在那时引用不是一个选项。之后,对语言的这种更改将向后不兼容。

std::function 存储并可以 "invoke any CopyConstructible Callable target".

Callable named requirement定义如下:

 if f is a pointer to member function of class T: 

    If std::is_base_of<T, std::remove_reference_t<decltype(t1)>>::value
    is true, then INVOKE(f, t1, t2, ..., tN) is equivalent to
    (t1.*f)(t2, ..., tN)

或者,换句话说:如果在删除第一个参数上的任何引用限定符后,最终结果是成员函数的 class(或子 class),则调用成员函数class.

的实例
otherwise, if t1 does not satisfy the previous items,
then INVOKE(f, t1, t2, ..., tN) is equivalent to
((*t1).*f)(t2, ..., tN).

否则:祈祷吧,希望第一个参数是指向成员函数 class 实例的指针,并通过给定的指针调用它(希望它是一个指针,或其他东西)假装它是一个指针)。

最终结果:两者等价。