Массив функций — членов класса в C

#c

Вопрос:

Я использую C 17 . Я пытаюсь сохранить функции членов класса в a vector . У меня есть класс, в котором у меня есть несколько функций с одинаковой сигнатурой. Мой сценарий состоит в том, чтобы хранить их в a vector и выполнять один за другим. Рассмотрим этот пример:

 class Test
{
    int Func(const stringamp; input, int index)
    {
        return 0;
    }

public:

    void Execute()
    {
        vector<function<int(const stringamp;, int)>> exes;
        exes.push_back(Test::Func);
    }
};

int main()
{
    Test test;
    test.Execute();

    cout << "Hello World!" << endl;
    return 0;
}
 

Когда я пытаюсь скомпилировать его, я получаю следующую ошибку:

 error: invalid use of non-static member function 'int Test::Func(const stringamp;, int)'
         exes.push_back(Test::Func);
                        ~~~~~~^~~~
main.cc" data-line="13" data-column="9">main.cc:13:9: note: declared here
     int Func(const stringamp; input, int index)
 

Как я могу достичь этого в C 17 ?

Ответ №1:

Нестатические функции-члены класса не похожи на автономные функции и статические функции-члены. Они требуют, чтобы объект работал против: неявного this указателя; вещи слева от . (или -> ) при обычном вызове. То есть, типа amp;Test::Func нет int(*)(const std::stringamp;, int) , это так int(Test::*)(const std::stringamp;, int) .

Это означает, что Test::Func это несовместимо с std::function<int(const stringamp;, int)> . То, с чем он совместим, — это std::function<int(Testamp;, const std::stringamp;, int)> или std::function<int(Test*, const std::stringamp;, int)> . std::function позаботится о специальном вызове, необходимом для вызова указателя на функцию-член для вас, и сделает все правильно в зависимости от типа ее первого параметра (либо указателя, либо ссылки).

Например, следующее будет работать нормально:

 std::function<int(Testamp;, const std::stringamp;, int)> func{amp;Test::Func};
Test foo;
std::cout << func(foo, "hello", 42) << 'n';
 

Если вы не хотите предоставлять Test объект на сайте вызова, вы можете либо создать Func static , и в этом случае он будет вести себя точно как автономная функция, либо использовать лямбда-код, либо std::bind создать std::function оболочку функционального объекта, в которой хранится указатель или ссылка на Test экземпляр или на весь Test экземпляр:

 Test foo;
std::function<int(const std::stringamp;, int)> func{
    [amp;foo](const std::stringamp; str, int i) {
        return foo.Func(str, i);
    }
};

// func holds a reference to foo, so no need to pass it
std::cout << func("hello", 42) << 'n';
 

Однако будьте осторожны, делая это. Если вы записываете ссылку или указатель на объект, этот объект должен быть все еще жив при std::function вызове оболочки.

Ответ №2:

Нестатические функции-члены класса всегда this неявно передают указатель в качестве аргумента. Отсюда и ошибка компилятора. Вам придется привязать функцию-член к экземпляру класса, чтобы передать this указатель.

В вашем случае, если у вас есть такой класс, как:

 class MyClass
{
    public:
        int Func(const std::stringamp; input, int index)
        {
            std::cout << input << " " << index << std::endl;
            return 0;
        }
};
 

вы можете добавить функциональные объекты в свой вектор, как это, используя std::bind или лямбда:

 std::vector<std::function<int(const std::stringamp;, int)>> exes;
MyClass obj;
//Using std::bind
exes.push_back(std::bind(amp;MyClass::Func, amp;obj, std::placeholders::_1, std::placeholders::_2));
//Or a lambda
exes.push_back([amp;obj](const std::stringamp; str, int n) { return obj.Func(str, n); });
 

Затем вы можете вызывать свои функциональные объекты с параметрами:

 for (auto amp;fn : exes)
{
    fn("hello", 1);
}
 

ДЕМОНСТРАЦИЯ

Ответ №3:

было бы лучше, если бы у вас был глобальный объект «exec» или

 std::vector<class Exec> exes;
 

затем вы могли бы вернуть ссылки на объект класса, например:

 class Exec {
public:
   Exec();
  ~Exec();
   void foo();
};

std::vector<class Exec> exes(10);  // init: 10 elements

int main(int argc, char **argv) {
   auto *ptr1 = new Exec;
   exes.push_back(ptr1);
   ...
}
 

Комментарии:

1. Спасибо за предложение. Но этого я не хотел.

2. ладно, я больше стандартной старой школы C программист, и не знаю, все новые понятия по C 17, чтобы вы могли выйти из станд::функция издержек путем объявления с typedef в анонимной функции, как: определение типа (*инт)(СТД::строка,инт) func_wrapper; а затем получить к ним доступ с func_wrapper = amp;func_address; , а затем вы могли бы назвать это как: func_wrapper(«hellp»,42); . другими словами, вы сохраняете позицию в памяти func_wrapper в std::вектор<std::unique_ptr><std::unique_ptr> . так как все функции имеют одинаковую подпись …

3. Да, это работает, за исключением случаев, когда функция является частной функцией класса.

4. ты прав. C — это строгое программирование с проблемами безопасности. C-это более процедурное программирование, где почти все открыто в глобальном масштабе (см. C и его искаженные члены). Но вы можете предоставить элементы getter и setter для свойств чтения/записи. В качестве альтернативы вы можете использовать «struct»s как «класс» — исключение составляет than. в вашей структуре все члены и переменные являются общедоступными.