Переменные лямбда-выражений С++ в классах

Я хочу сохранить переменные лямбда-выражений (как в первом блоке кода). Проблема в том, что когда я использую классы (например, второй блок кода), компилятор возвращает мне некоторые ошибки. Я не знаю, как это исправить.

Я надеюсь, что кто-нибудь может мне помочь и объяснить, почему это не работает так. Спасибо.

Первый код:

// variable for function pointer
void (*func)(int);
// default output function
void my_default(int x) {
    cout << "x =" << "\t" << x << endl << endl;
}


int main() {
    cout << "Test Programm\n\n";

    // 1. Test - default output function
    cout << "my_default\n";
    func = &my_default;
    func(5);

    // 2. Test - special output function 2
    cout << "my_func2\n";
    func = [](int x) {  cout << "x =" << "  " << x << endl << endl; };
    func(5);

    return 0;
}

Второй код:

class test {
private:
    // variable for function pointer
    void (*func)(int);
    // default output function
    void my_default(int x) {
        cout << "x =" << "\t" << x << endl << endl;
    }

public:
    void dummy(void) {
        // 1. Test - default output function
        cout << "my_default\n";
        func = &my_default;
        func(5);

        // 2. Test - special output function 2
        cout << "my_func2\n";
        func =  [](int x)->int{ cout << "x =" << "  " << x << endl << endl; };
        func(5);
    }
};


// entry
int main() {
    cout << "Test Programm\n\n";

    test a;
    a.dummy();

    return 0;
}

Компилятор:

pi@raspberrypi ~/dev/property $ gcc -std=c++0x -o test2 test2.cpp -lstdc++
test2.cpp: In member function ‘void test::dummy()’:
test2.cpp:491:17: error: ISO C++ forbids taking the address of an unqualified or parenthesized non-static member function to form a pointer to member function.  Say ‘&test::my_default’ [-fpermissive]
test2.cpp:491:17: error: cannot convert ‘void (test::*)(int)’ to ‘void (*)(int)’ in assignment
test2.cpp:496:77: error: invalid user-defined conversion from ‘test::dummy()::<lambda(int)>’ to ‘void (*)(int)’ [-fpermissive]
test2.cpp:496:28: note: candidate is: test::dummy()::<lambda(int)>::operator int (*)(int)() const <near match>
test2.cpp:496:28: note:   no known conversion for implicit ‘this’ parameter from ‘int (*)(int)’ to ‘void (*)(int)’

person Heinrich    schedule 28.01.2014    source источник
comment
Указатели на функции-члены — это не то же самое, что указатели на функции. Вы можете видеть в ошибке, что типы разные.   -  person chris    schedule 29.01.2014
comment
В дополнение к ответам ниже... Ваша лямбда в test2.cpp дает сбой, потому что возвращает int вместо void.   -  person Rastaban    schedule 29.01.2014


Ответы (2)


Проблема в том, что функции-члены не являются обычными функциями, их нельзя присвоить указателю на функцию, потому что тип, членом которого они являются, является частью их типа. Кроме того, функция-член должна иметь объект для вызова, который будет this внутри кода функции.

У вас есть несколько решений:

  1. разрешить только функции, которые являются членами вашего класса

    void (*MyClass::func)(int); // but you can use it only with members of the class
    
  2. используйте std::function

    typedef std::function<void(int)> func;
    

Решение 2 является самым простым, поскольку std::function предназначен для работы со всем, что можно вызывать с той же сигнатурой, что и в параметрах шаблона. Кроме того, это единственное решение, позволяющее хранить замыкания (объекты из лямбда-выражений). Дополнительные сведения см. в разделе обратные вызовы в стиле C++11?.

class test {
private:
    // variable for function pointer
    std::function< void ( int )> func;
    // default output function
    void my_default(int x) {
        cout << "x =" << "\t" << x << endl << endl;
    }

public:
    void dummy(void) {
        // 1. Test - default output function
        cout << "my_default\n";
        func = std::bind(&test::my_default, this, std::placeholders::_1);
        // or 
        func = [&]( int i ){ my_default( i ); };
        func(5);

        // 2. Test - special output function 2
        cout << "my_func2\n";
        func =  [](int x)->int{ cout << "x =" << "  " << x << endl << endl; };
        func(5);
    }
};


// entry
int main() {
    cout << "Test Programm\n\n";

    test a;
    a.dummy();

    return 0;
}
person Klaim    schedule 28.01.2014
comment
Спасибо за объяснение. func = std::bind(&my_default, this); вернет ошибки (компилятор), но, как написал Брайан Би, std::bind(&test::my_default, this, std::placeholders::_1); не вернет ошибок. - person Heinrich; 29.01.2014
comment
Ах да, мне всегда трудно вспомнить, как использовать привязку, поэтому я просто использую лямбда-выражения. Также есть mem_fun(). - person Klaim; 29.01.2014
comment
Я рекомендую маршрут std::function, хотя третий вариант (в зависимости от природы my_default) состоит в том, чтобы сделать my_default статическим. - person Rastaban; 29.01.2014
comment
@Rastaban Он хочет использовать лямбда-выражения, и мы не знаем, захватывает ли он лямбда-выражения или нет. - person Klaim; 29.01.2014

Функция-член отличается от обычной функции тем, что для ее вызова должен быть доступен экземпляр класса (т. е. объект, который станет *this). Вы не можете сделать так, чтобы обычная переменная-указатель на функцию указывала на функцию-член.

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

void (test::*func)(int);

заявить об этом,

func = &test::my_default;

назначить его и

(this->*func)(5);

назвать это. Конечно, теперь вы не можете сделать так, чтобы указатель функции-члена указывал на лямбду.

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

std::function<void(int)> func;

связать следующим образом:

func = std::bind(&test::my_default, this, std::placeholders::_1);

а потом звонить нормально. std::function работает с лямбда-выражениями так же, как указатель функции.

person Brian Bi    schedule 28.01.2014
comment
Спасибо!! Я, наверное, забыл, в чем разница между функцией-членом и обычной функцией. - person Heinrich; 29.01.2014