Какая сигнатура функции функции-члена?

У меня возникают проблемы с пониманием сигнатур функций и указателей.

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::*.

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