Какая сигнатура функции функции-члена?
У меня возникают проблемы с пониманием сигнатур функций и указателей.
struct myStruct
{
static void staticFunc(){};
void nonstaticFunc(){};
};
int main()
{
void (*p)(); // Pointer to function with signature void();
p = &myStruct::staticFunc; // Works fine
p = &myStruct::nonstaticFunc; // Type mismatch
}
Мой компилятор говорит, что тип myStruct::nonstaticFunc()
равен void (myStruct::*)()
, но не тот тип указателя, указывающий на него?
Я спрашиваю, потому что когда вы создаете объект std::function
, вы передаете подпись функции функции, на которую хотите ее указать, например:
std::function<void()> funcPtr; // Pointer to function with signature void()
not
std::function<void(*)()> funcPtr;
Если бы мне пришлось угадать, основываясь на шаблоне void()
, я бы сказал:
void myStruct::();
or
void (myStruct::)();
Но это не так. Я не понимаю, почему я должен добавить звездочку только потому, что она нестатическая, а не статическая. Другими словами, указатель void(* )()
указывает на функцию с сигнатурой void()
, а указатель void(myStruct::*)()
указывает на функцию с сигнатурой, что?
Ответы
Ответ 1
Мне кажется, что есть основное недопонимание того, что такое указатель на член. Например, если у вас есть:
struct P2d {
double x, y;
};
указатель-член double P2d::*mp = &P2d::x;
не может указывать на координату x
экземпляра специфический P2d
, он вместо этого является "указателем" на имя x
: для получения двойного вам нужно предоставить экземпляр P2d
, который вы ищете... например:
P2d p{10, 20};
printf("%.18g\n", p.*mp); // prints 10
То же самое относится к функциям-членам... например:
struct P2d {
double x, y;
double len() const {
return sqrt(x*x + y*y);
}
};
double (P2d::*f)() const = &P2d::len;
где f
не является указателем на функцию-член конкретного экземпляра, и ему требуется this
для вызова с
printf("%.18g\n", (p.*f)());
f
Другими словами, это просто "селектор" , который из константных функций класса P2d
не принимает никаких параметров и возвращает double
, вас интересует. В этом конкретном случае (поскольку имеется только одна функция-член) такой селектор может быть сохранен с использованием нулевых битов (единственное возможное значение, которое вы можете установить для этого указателя: &P2d::len
).
Пожалуйста, не стесняйтесь, если сначала не поймете указателей участников. Они действительно являются "странными", и не многие программисты на C++ понимают их.
Честно говоря, они также не очень полезны: наиболее часто требуется указатель на метод конкретного экземпляра.
С++ 11 предусматривает, что с std::function
оберткой и lambdas:
std::function<double()> g = [&](){ return p.len(); };
printf("%.18g\n", g()); // calls .len() on instance p
Ответ 2
std::function<void()> funcPtr = std::bind(&myStruct::nonstaticFunc, obj);
Как хранить функцию-член в std::function
. Функция-член должна быть вызвана на действительный объект.
Если вы хотите отложить передачу объекта до конца, вы можете выполнить его следующим образом:
#include <functional>
#include <iostream>
struct A {
void foo() { std::cout << "A::foo\n"; }
};
int main() {
using namespace std::placeholders;
std::function<void(A&)> f = std::bind(&A::foo, _1);
A a;
f(a);
return 0;
}
std::bind
позаботится о деталях для вас. std::function
все еще должен иметь подпись регулярной функции по мере ее ввода. Но он может маскировать элемент, если объект создается как параметр функции.
Addenum:
Для назначения в std::function
вам даже не нужно std::bind
для позднего связывания объекта, если прототип верен:
std::function<void(A&)> f = &A::foo;
Ответ 3
p = &myStruct::staticFunc; // Works fine
p = &myStruct::nonstaticFunc; // Type mismatch
Причина. Преобразование от функции к указателю никогда не применяет к нестационарным функциям-членам, поскольку значение l, которое относится к нестатической функции-члену
не может быть получено.
pointer void (*)() указывает на функцию с сигнатурой void(), а указатель void (myStruct:: *)() указывает на функцию с сигнатурой, что?
myStruct::
заключается в том, чтобы вызывать нестационарную функцию-член struct myStruct
(а не другие структуры, как показано ниже):
struct myStruct
{
static void staticFunc(){};
void nonstaticFunc(){};
};
struct myStruct2
{
static void staticFunc(){};
void nonstaticFunc(){};
};
int main()
{
void (*p)(); // Pointer to function with signature void();
void (myStruct::*f)();
p = &myStruct::staticFunc; // Works fine
p = &myStruct2::staticFunc; // Works fine
f = &myStruct::nonstaticFunc; // Works fine
//f = &myStruct2::nonstaticFunc; // Error. Cannot convert 'void (myStruct2::*)()' to 'void (myStruct::*)()' in assignment
return 0;
}
Ответ 4
Когда вы используете указатель, std:: function или std:: bind ссылаются на нестационарную функцию-член (а именно, "метод" класса Foo), первый параметр должен быть конкретным объектом класса Foo, потому что нестатический метод должен вызываться конкретным объектом, а не классом.
Подробнее: std:: function и
std:: bind.
Ответ 5
Ответ находится в doc.
Указатель на объявление-член: объявление S C::* D;
объявляет D
как указатель на нестатический член C
типа, определяемый decl-specifier-seq S
.
struct C
{
void f(int n) { std::cout << n << '\n'; }
};
int main()
{
void (C::* p)(int) = &C::f; // pointer to member function f of class C
C c;
(c.*p)(1); // prints 1
C* cp = &c;
(cp->*p)(2); // prints 2
}
Нет функции с сигнатурой void ()
. Для функции или void (foo::*)()
существует void (*)()
для метода foo
. Звездочка обязательна, потому что указатель на x. std::function
не имеет к этому никакого отношения.
Примечание. Ваша путаница в том, что void()
- та самая подпись, что void (*)()
. Или даже int()
<= > int (*)()
. Возможно, вы думаете, что можете написать int (foo::*)
для указателя метода. Но это указатель элемента данных, поскольку скобки являются необязательными, int (foo::*)
<= > int foo::*
.
Чтобы избежать такого неясного синтаксиса, вам нужно написать свой указатель на функцию/элемент с типом возврата, звездочкой и его параметрами.