#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. в вашей структуре все члены и переменные являются общедоступными.