如何使用智能指针对象执行成员函数指针?

9
我试图使用智能指针实例调用类的成员函数。传递的是该函数的地址,我想通过相应类的智能指针实例来调用它。我已经尝试过以下方法:
(registerList.*setRegister)();

但是它会出现错误:

no match for ‘operator->*

Register 类成员函数:

uint16_t Registers::getSP()
{
    return this->sp;
}

代码片段如下:
std::unique_ptr<Registers> registerList;

SetRegisteropcodeLdWordRegister(&Registers::getSP)

void opcodeLdWordRegister(uint16_t (*Registers::setRegister)()) 
{
        (registerList.*setRegister)();
}
2个回答

5
首先,您展示的代码与错误信息不匹配。对于给定的代码 您应该收到以下错误消息(来自clang)
error: left hand operand to .* must be a class compatible with the right hand operand, but is 'std::unique_ptr<Registers>'    
   (registerList.*setRegister)();
            ^

这可以通过取消指针引用来解决(就像@Caleth的回答中所示):
((*registerList).*setRegister)();

现在在问题中显示的错误信息:no match for ‘operator->*应该在尝试以下语法时出现。(最小可重现代码)
(registerList->*setRegister)();

这是因为智能指针在标准中没有定义成员访问运算符。因此,您需要通过operator*或成员函数std::unique_ptr::get对智能指针进行解引用,并调用成员函数。

使用成员std::unique_ptr::get的正确语法应该是在线查看演示

(registerList.get()->*setRegister)()

说到这里,如果你有访问的权限,使用统一版本的函数调用器std::invoke来调用相应实例的成员函数,这样你就可以忘记operator->*可能会很复杂的语法了。
你的代码也有几个问题:
  • The member function pointer type in your opcodeLdWordRegister is wrong.It should have been

    return_type(Class_name::* variable_name)(/*parameters_types, if any*/)
    

    Following is the fixed version.

    #include <functional>  // std::invoke
    
    void opcodeLdWordRegister(uint16_t(Registers:: * setRegister)())
    //                                 ^^^^^^^^^^^^^^^^^^^^^^^^^ >>> correct syntax
    {   
        std::invoke(setRegister, registerList);
        //          ^^^^^^^^^^^                 member function
        //                       ^^^^^^^^^^^^   instance
     }
    
  • Secondly, dereferencing of the uninitialised registerList pointer will cause UB.

以下是一个演示性的最小完整示例,适用于此情况:(在线查看演示)

#include <iostream>
#include <functional>  // std::invoke
#include <memory>      // std::unique_ptr

class Registers
{
   uint16_t sp{2};        // member initialized with 2
public:
   uint16_t getSP() const // function can be marked const as it does not alter the member
   {
      return this->sp;
   }
};

auto registerList = std::make_unique<Registers>(); // initialized the pointer with default object

void opcodeLdWordRegister(uint16_t(Registers::*setRegister)() const)
//                                 ^^^^^^^^^^^^^^^^^^^^^^^^^  correct syntax
{
   std::cout << std::invoke(setRegister, registerList);
}

int main()
{
   opcodeLdWordRegister(&Registers::getSP);
   return 0;
}

输出:

2

应该是 *registerList 吧? - Evg
2
@Evg 我认为这不是必要的:https://wandbox.org/permlink/jz9jQQfdJj6R0fjO - JeJo
2
我明白了。std::invoke会为我们启动星号。 - Evg
3
std::invoke 的重点在于统一调用语法。在这种情况下,它的含义是:"如果t1不满足前面的条件,则INVOKE(f, t1, t2, ..., tN)等同于((*t1).*f)(t2, ..., tN)。" - Caleth

3
如果您没有std::invoke,您仍然可以执行它所执行的操作,即在调用点中取消引用(唯一)指针。
((*registerList).*setRegister)();

智能指针必须实现 operator->* 才能使用第二种语法。标准库类型不支持该操作符。 - StoryTeller - Unslander Monica
@StoryTeller-UnslanderMonica,这是为什么呢? - Evg
1
@Evg 是因为“每个人”都会忘记 ->* 存在的吗? - Caleth
@Evg - 如果我要猜的话,我会说这是因为在C++11中处理成员函数和成员数据有点困难(当智能指针被添加时)。在当时,添加这个小众功能的成本效益比不太好。在C++17中,这变得更简单了,但我认为这并没有引起任何人的注意。 - StoryTeller - Unslander Monica
1
@Evg 相关:https://dev59.com/OVYM5IYBdhLWcg3wmxCl - HolyBlackCat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接