Как я могу передать функцию членства класса как обратный вызов?

Неустранимая ошибка: [TraitA] и [TraitB] определяют одно и то же свойство ([$ x]) в композиции [ClassC]

Происходит, когда класс пытается use несколько Черты , где две или более из этих признаков имеют , определяют свойство с тем же именем и с свойством, имеющим разные начальные значения.

Пример:

Проблемное: хотя можно разрешить конфликты между конкурирующими методами , в настоящее время нет синтаксиса, который бы разрешил конфликт между двумя конкурирующими свойствами. Единственное решение в это время - refactor ; т.е. избежать конфликта между именами свойств, которые приводят к фатальной ошибке.


Вопросы, относящиеся:

66
задан rsjaffe 4 January 2019 в 23:35
поделиться

8 ответов

Это не работает, потому что указатель функции членства не может быть обработан как нормальный указатель функции, потому что он ожидает "этот" объектный аргумент.

Вместо этого можно передать статическую функцию членства следующим образом, которые похожи на нормальные функции лица, не являющегося членом какой-либо организации, в этом отношении:

m_cRedundencyManager->Init(&CLoggersInfra::Callback, this);

функция может быть определена следующим образом

static void Callback(int other_arg, void * this_pointer) {
    CLoggersInfra * self = static_cast<CLoggersInfra*>(this_pointer);
    self->RedundencyManagerCallBack(other_arg);
}
47
ответ дан Johannes Schaub - litb 24 November 2019 в 14:52
поделиться

Это - простой вопрос, но ответ удивительно сложен. Короткий ответ - Вы, может сделать то, что Вы пытаетесь сделать со станд.:: bind1st или повышение:: связать. Более длинный ответ ниже.

компилятор корректен, чтобы предложить, чтобы Вы использовали & CLoggersInfra:: RedundencyManagerCallBack. Во-первых, если RedundencyManagerCallBack является функцией членства, сама функция не принадлежит никакому конкретному экземпляру класса CLoggersInfra. Это принадлежит самому классу. Если Вы когда-либо вызывали статическую функцию класса прежде, Вы, возможно, заметили использование того же SomeClass:: синтаксис SomeMemberFunction. Так как сама функция 'статична' в том смысле, что она принадлежит классу, а не конкретному экземпляру, Вы используете тот же синтаксис. '&'; необходимо, потому что с технической точки зрения Вы не передаете функции непосредственно - функции не являются реальными объектами в C++. Вместо этого Вы технически передаете адрес памяти для функции, то есть, указатель туда, где инструкции функции начинаются в памяти. Последствием является то же, хотя, Вы эффективно 'передаете функцию' в качестве параметра.

, Но это - только половина проблемы в этом экземпляре. Как я сказал, RedundencyManagerCallBack, функция не 'принадлежит' никакому конкретному экземпляру. Но это кажется, что Вы хотите передать его как обратный вызов с конкретным экземпляром в памяти. Чтобы понять, как сделать это, необходимо понять, каковы функции членства действительно: регулярный not-defined-in-any-class функционирует с дополнительным скрытым параметром.

, Например:

class A {
public:
    A() : data(0) {}
    void foo(int addToData) { this->data += addToData; }

    int data;
};

...

A an_a_object;
an_a_object.foo(5);
A::foo(&an_a_object, 5); // This is the same as the line above!
std::cout << an_a_object.data; // Prints 10!

, Сколько параметров делает A:: нечто берут? Обычно мы сказали бы 1. Но под капотом, нечто действительно берет 2. Рассмотрение A:: определение нечто, этому нужен определенный экземпляр для 'этого' указателя, чтобы быть значимым (компилятор должен знать то, что 'это'). Путем Вы обычно определяете то, что Вы хотите, 'это', чтобы быть является через синтаксис MyObject. MyMemberFunction (). Но это - просто синтаксический сахар для передачи адреса MyObject как первый параметр к MyMemberFunction. Так же, когда мы объявляем функции членства в определениях классов, мы не помещаем 'это' в список параметров, но это - просто подарок от разработчиков языка для сохранения ввода. Вместо этого необходимо определить, что функция членства статична для выбора из него, автоматически получая дополнительное 'этот' параметр. Если компилятор C++ перевел вышеупомянутый пример в код C (исходный компилятор C++ на самом деле проложил себе путь), это, вероятно, запишет что-то вроде этого:

struct A {
    int data;
};

void a_init(A* to_init)
{
    to_init->data = 0;
}

void a_foo(A* this, int addToData)
{ 
    this->data += addToData;
}

...

A an_a_object;
a_init(0); // Before constructor call was implicit
a_foo(&an_a_object, 5); // Used to be an_a_object.foo(5);

Возврат Вашему примеру, существует теперь очевидная проблема. 'Init' хочет указатель на функцию, которая берет один параметр. Но & CLoggersInfra:: RedundencyManagerCallBack является указателем на функцию, которая берет два параметра, это - нормальный параметр и секрет 'этот' параметр. Таким образом, почему Вы все еще получаете ошибку компилятора (как примечание стороны: Если Вы когда-либо использовали Python, этот вид беспорядка - то, почему 'сам' параметр требуется для всех функций членства).

подробный способ обработать это должно создать специальный объект, который содержит указатель на экземпляр, который Вы хотите, и назвали функцию членства, что-то любит 'выполненный' или 'выполняется' (или перегружается' ()' оператор), который берет параметры для функции членства, и просто называет функцию членства с теми параметрами на сохраненном экземпляре. Но это потребовало бы, чтобы Вы изменили 'Init' для взятия специального объекта, а не необработанного указателя функции, и это кажется, что Init является чужим кодом. И создание специального класса в течение каждого раза эта проблема подходит, будет вести для кодирования чрезмерного увеличения размера.

Поэтому теперь, наконец, хорошее решение, повышение:: свяжите и повысьте:: функция, документация для каждого можно найти здесь:

повышение:: свяжите повышение документов , :: функциональные документы

повышение:: свяжите позволит Вам взять функцию и параметр к той функции, и сделать новую функцию, где тот параметр 'заблокирован' на месте. Таким образом, если у меня есть функция, которая добавляет два целых числа, я могу использовать повышение:: свяжите для создания новой функции, где один из параметров заблокирован для высказывания 5. Эта новая функция только возьмет один целочисленный параметр и будет всегда добавлять 5 конкретно к нему. Используя эту технику, можно 'привязать' скрытое 'этот' параметр, чтобы быть конкретным экземпляром класса и генерировать новую функцию, которая только берет один параметр, точно так же, как Вы хотите (обратите внимание, что скрытый параметр всегда первый параметр, и нормальные параметры существуют порядка после него). Посмотрите на повышение:: свяжите документы для примеров, они даже конкретно обсуждают использование его для функций членства. Технически существует стандартная функция, вызванная станд.:: bind1st, который Вы могли использовать также, но повышение:: свяжите является более общим.

, Конечно, существует только еще одна выгода. повышение:: свяжите сделает хорошее повышение:: функция для Вас, но это все еще технически не, необработанный указатель функции как Init, вероятно, хочет. К счастью повышение позволяет преобразовывать повышение:: функция к необработанным указателям, как зарегистрировано на StackOverflow здесь . Как это реализует, это выходит за рамки этого ответа, хотя это интересно также.

не волнуются, кажется ли это нелепо твердым - Ваш вопрос пересекает несколько из более темных углов C++ и повышение:: свяжите невероятно полезно, как только Вы изучаете его.

C++ 11 обновлений: Вместо повышения:: свяжите Вас, может теперь использовать функцию лямбды, которая получает 'это'. Это в основном имеет компилятор, генерируют то же самое для Вас.

110
ответ дан Joseph Garvin 24 November 2019 в 14:52
поделиться

Что аргумент делает Init, берут? Каково новое сообщение об ошибке?

указатели Метода в C++ немного трудно использовать. Помимо самого указателя метода, также необходимо обеспечить указатель экземпляра (в случае this). Возможно Init ожидает его как отдельный аргумент?

3
ответ дан Konrad Rudolph 24 November 2019 в 14:52
поделиться

Указатель на функцию членства класса не является тем же как указателем на функцию. Участник класса берет неявный дополнительный аргумент ( этот указатель) и использует различное соглашение о вызовах.

, Если Ваш API ожидает функцию обратного вызова лица, не являющегося членом какой-либо организации, это - то, что необходимо передать ему.

3
ответ дан jalf 24 November 2019 в 14:52
поделиться

[Действительно ли 113] в состоянии использовать функции членства? Большинство обратных вызовов настраивается для использования регулярных функций или статических функций членства. Смотрите на эта страница в FAQ C++, Облегченном для получения дополнительной информации.

Обновление: объявление функции Вы обеспечили шоу, который m_cRedundencyManager ожидает функцию формы: void yourCallbackFunction(int, void *). Функции членства поэтому недопустимы как обратные вызовы в этом случае. Статический май функции членства работа, но если бы это недопустимо в Вашем случае, следующий код, также работал бы. Обратите внимание, что это использует злой бросок от void *.


// in your CLoggersInfra constructor:
m_cRedundencyManager->Init(myRedundencyManagerCallBackHandler, this);

// in your CLoggersInfra header:
void myRedundencyManagerCallBackHandler(int i, void * CLoggersInfraPtr);

// in your CLoggersInfra source file:
void myRedundencyManagerCallBackHandler(int i, void * CLoggersInfraPtr)
{
    ((CLoggersInfra *)CLoggersInfraPtr)->RedundencyManagerCallBack(i);
}
3
ответ дан e.James 24 November 2019 в 14:52
поделиться

Я вижу, что init имеет следующее переопределение:

Init(CALLBACK_FUNC_EX callback_func, void * callback_parm)

, где CALLBACK_FUNC_EX

typedef void (*CALLBACK_FUNC_EX)(int, void *);
1
ответ дан Alexis Wilke 24 November 2019 в 14:52
поделиться

Этот вопрос и ответ от FAQ C++, Облегченный покрытия Ваш вопрос и соображения, вовлеченные в ответ вполне приятно, я думаю. Короткий отрывок от веб-страницы я связался:

Don’t.

, поскольку функция членства бессмысленна без объекта вызвать его на, Вы, can’t делают это непосредственно (если бы X-оконная система была переписана в C++, это, вероятно, передало бы ссылки на объекты вокруг, не только указатели на функции; естественно объекты воплотили бы необходимую функцию и вероятно намного больше).

0
ответ дан Onorio Catenacci 24 November 2019 в 14:52
поделиться

Простое решение "обходное решение" все еще состоит в том, чтобы создать класс виртуальных функций, "соединяют интерфейсом" и наследовали его в классе вызывающей стороны. Затем передайте, это в качестве параметра "могло быть в конструкторе" другого класса, что Вы хотите перезвонить своему классу вызывающей стороны.

ОПРЕДЕЛЯЮТ Интерфейс:

class CallBack 
{
   virtual callMeBack () {};
};

Это - класс, что Вы хотите перезвонить Вам:

class AnotherClass ()
{
     public void RegisterMe(CallBack *callback)
     {
         m_callback = callback;
     }

     public void DoSomething ()
     {
        // DO STUFF
        // .....
        // then call
        if (m_callback) m_callback->callMeBack();
     }
     private CallBack *m_callback = NULL;
};

И это - класс, который будет призван обратно.

class Caller : public CallBack
{
    void DoSomthing ()
    {
    }

    void callMeBack()
    {
       std::cout << "I got your message" << std::endl;
    }
};
0
ответ дан 24 November 2019 в 14:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: