【问题标题】:why std::function that store a call to member function can have two different callable type为什么存储对成员函数的调用的 std::function 可以有两种不同的可调用类型
【发布时间】:2021-10-27 07:35:13
【问题描述】:
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&amp; 作为与this 指针类型不匹配的参数。为什么上面的代码不会产生编译错误?

【问题讨论】:

  • func 将在内部存储一个 const 指针,func2 将在内部存储一个 const 引用。两者都是有效的,并且引用和指针都是通过获取地址(& 运算符)来初始化的。不过,周围可能有人可以用更好的标准来解释它;)
  • func(&amp;f, 2) 类似于(&amp;f)-&gt;print_add(2)func2(f2, 3) 类似于f2.print_add(3)
  • 我不确定,但std::function 可能会使用std::invoke 机制。
  • @Eljay 该标准不强制std::function 使用std::invoke,但std::function 上的呼叫操作员规范与std::invoke 的规范非常相似,并使用INVOKE,这是标准为 Callable 对象定义的操作。
  • @Eljay std::function 是根据(虚构的)INVOKE 表达式定义的,具体化为std::invoke

标签: c++ methods callable


【解决方案1】:

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)

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

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

否则:交叉手指,希望第一个参数是指向成员函数类的实例的指针,并通过给定的指针调用它(希望它是一个指针,或者假装它是一个指针)。

最终结果:任何一个都等价于另一个。

【讨论】:

  • "希望第一个参数是指针"或者带有operator*的东西,比如std::unique_ptr
  • 采用const Foo&amp; 的方法特别有趣,因为它可以用来创建一个临时的Foo 并对其进行调用。所以func2(10, 20); 也是有效的,尽管它可能看起来很混乱。
【解决方案2】:

为什么func和func2都能正确调用成员函数Foo::print_add?

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

据我所知,“this”指针作为隐藏参数传递

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

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


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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-16
    • 1970-01-01
    • 2017-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多