Подтвердить что ты не робот

Могу ли я создавать анонимные классы на С++ и захватывать внешние переменные, например, в Java?

В Java, когда мне нужна функция обратного вызова, я должен реализовать анонимный класс. Внутри анонимного класса я могу получить доступ к внешним переменным, если они final.

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

Я попробовал следующий пример. Он работает с GCC 4.3.4.

class IA {
public:
  virtual int f(int x) = 0;  
};

int main() {
    class : public IA {
        int f(int x) { return x + 1; }
    } a;
    doFancyWork(&a);
    return 0;
}

Можно ли захватить внешние переменные, подобные этому?

int main() {
    int y = 100; // mark y as final if possible
    class : public IA {
        int f(int x) { return x + y; }
    } a;
    return 0;
}

UPDATE:

Второй пример не будет компилироваться. Ошибки здесь,

prog.cpp: In member function ‘virtual int main()::<anonymous class>::f(int)’:
prog.cpp:9: error: use of ‘auto’ variable from containing function
prog.cpp:7: error:   ‘int y’ declared here
prog.cpp: In function ‘int main()’:
prog.cpp:7: warning: unused variable ‘y’

UPDATE:

Я только что осознал еще несколько проблем:

  • Я не могу написать конструктор, потому что класс не имеет имени
  • список инициализаторов не позволяет наследовать.
  • любое изменение для компиляции делает код нечитаемым.

Думаю, мне нужно отойти от анонимных классов.

4b9b3361

Ответ 1

Нет возможности автоматически фиксировать эти переменные, но вы можете использовать альтернативный подход. Это если вы хотите захватить по ссылке:

int main() {
    int y = 100; // mark y as final if possible
    class IB : public IA {
    public:
      IB(int& y) : _y(y) {}
      int f(int x) { return x + _y; }
    private:
      int& _y;
    } a (y);
    return 0;
}

Если вы хотите записать по значению, просто измените int& на int.

Во всяком случае, вы можете использовать кортеж lambdas как объект с несколькими обратными вызовами, если это вас беспокоит о отдельных лямбдах. У вас все равно будет все, что упаковано в один объект, и захват будет сделан бесплатно.

Как пример:

auto callbacks = make_tuple(
    [] (int x) { cout << x << endl; },
    [&] () { cout << y << endl; }, // y is captured by reference
    [=] (int x) { cout << x + y << endl; }, // y is captured by value
    // other lambdas here, if you want...
    );

Ответ 2

Вы можете захватить переменную вручную (что похоже на то, что делает захват лямбда за кулисами):

int main() {
    int y = 100;
    struct { 
        int& y;
        int operator()(int x) { return x + y; }
    } anon = { y };
}

Затем вы можете использовать его следующим образом:

#include <iostream>
...
std::cout << anon(10) << std::endl;

Печатает 110, как ожидалось. К сожалению, вы не можете наследовать анонимный тип от другого с помощью этого метода, поскольку конструктивные типы инициализатора не могут наследовать другого типа. Если наследование имеет решающее значение, вы должны использовать метод конструктора описанный Энди Проулом.

Ответ 3

С++ лямбда может захватывать "внешние" переменные. [Редактировать: когда я впервые прочитал вопрос, я как-то пропустил, где он упомянул, что знает о лямбдах. К лучшему или худшему, С++ не имеет ничего, что действительно напоминает анонимный класс].

Например:

#include <iostream>

int main(){ 

    int y = 100;
    auto lambda = [=](int x) { return x + y; };

    std::cout << lambda(2);
}

... выводит 102 в качестве вывода.

Обратите внимание, что хотя он выглядит несколько как функция, С++ лямбда действительно приводит к созданию класса. Полагаю, я должен добавить: этот класс не является технически анонимным, но имеет некоторое неуказанное имя, которое никогда не отображается напрямую.

Изменить: я все еще немного озадачен тем, что не использовал lambdas. Является ли намерение использовать один класс, который содержит много функций-членов? Если это так, неясно, как вы планируете указывать, какую функцию-член нужно вызывать, в какое время/для какой цели. Моя немедленная реакция заключается в том, что это звучит подозрительно, как будто вы пытаетесь перевернуть язык, чтобы поддержать проблемный дизайн.

Ответ 4

Это не анонимность класса, который ограничивает доступ к внешним переменным. В вопросе y недоступен, потому что класс был определен локально внутри функции.

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

Что касается анонимных классов, вы не можете иметь конструкторы и деструкторы. Все функции-члены должны быть объявлены внутри определения класса. Он не может содержать статические статические члены, в том числе константные статические интегральные элементы, которые обычно могут быть созданы внутри определения класса. Также наследование не допускается.

Анонимные классы являются неясным углом С++ и имеют мало практического значения. Лямбда-функции и другие методы намного более гибкие. Но кто знает, возможно, в некоторых ситуациях это могло бы помочь с чтением кода.

Ответ 5

Если ваш класс IA действительно имеет только один виртуальный метод, который вам нужно переопределить (и реальная сложность - это другие не виртуальные методы), но вы не хотите фиксировать локальные переменные, которые этот метод требует, как насчет это:

int main() {
  int y = 100;
  auto f = [=](int x){return x+y;};
  typedef decltype(f) F;
  struct IB : IA {
    F _f;
    IB(F _f): _f(_f) {}
    int f(int x) { return _f(x); }
  } a(f);
  doFancyWork(&a);
  return 0;
}