Как выполнить указатель на функцию-член с помощью объекта интеллектуального указателя? - PullRequest
6 голосов
/ 08 ноября 2019

Я пытаюсь выполнить member-function класса с экземпляром умного указателя. Адрес этой функции передается по значению, которое я хочу вызвать через экземпляр умного указателя соответствующего класса.

Я уже пробовал это:

(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 ]

4 голосов
/ 08 ноября 2019

Прежде всего, указанный вами код и сообщение об ошибке не совпадают. Для данного кода вы должны получить (из 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)()

При этом, если у вас есть доступ к используйте унифицированную версию функции invoker std::invoke для вызова функции-члена с соответствующим экземпляром, с помощью которого вы можете забыть о (возможно) сложном синтаксисе для operator->*.

Ваш код также имеет несколько проблем:

  • Тип указателя на функцию-член в вашем opcodeLdWordRegister неверен. Это должно было быть

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

    Ниже приводитсяисправленная версия.

    #include <functional>  // std::invoke
    
    void opcodeLdWordRegister(uint16_t(Registers:: * setRegister)())
    //                                 ^^^^^^^^^^^^^^^^^^^^^^^^^ >>> correct syntax
    {   
        std::invoke(setRegister, registerList);
        //          ^^^^^^^^^^^                 member function
        //                       ^^^^^^^^^^^^   instance
     }
    
  • Во-вторых, разыменование неинициализированного указателя registerList вызовет 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
3 голосов
/ 08 ноября 2019

Если у вас нет std::invoke, вы все равно можете сделать то, что он сделал бы, то есть разыменовать (уникальный) указатель на сайте вызова.

((*registerList).*setRegister)();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...