Я часто создаю локальные вспомогательные классы внутри методов, везде, где такой класс локально полезен, но не имеет значения вне метода. Я просто наткнулся на случай, когда мне хотелось бы, чтобы два локальных класса были взаимозависимыми.
Идея состоит в следующем:
void SomeClass::someMethod()
{
struct A
{
B * b;
void foo() { if(b) b->bar(); };
};
struct B
{
A * a;
void bar() { if(a) a->foo(); }
};
}
Но он не компилируется, потому что A
нуждается в B
Вперед объявление B
помогает так, чтобы линия B * b;
компиляции, но все же метод A::foo()
требует полного объявления B
и компилятор жалуется.
Я вижу два обходных пути:
Объявление и определение классов в SomeClass.cpp, до SomeClass::someMethod()
. Я чувствую, что это не изящно, поскольку не только классы не являются локальными для SomeClass::someMethod()
, но даже не локальны для SomeClass
.
Объявление классов в SomeClass.h, вложенных в SomeClass
, и определение их в SomeClass.cpp. Мне не нравится это решение либо потому, что не только классы не являются локальными для SomeClass::someMethod()
, но он загрязняет SomeClass.h без уважительной причины, кроме ограничения языка.
Отсюда два вопроса: возможно ли вообще, чтобы классы были локальными для SomeClass::someMethod()
? Если нет, вы видите более элегантные обходные пути?
Поскольку ответ кажется: "невозможно иметь чистые взаимозависимые локальные классы", оказывается, что обходной путь, который мне больше всего нравится, - это перемещать логику вне самих структур. Как было предложено remyabel в комментариях к этому вопросу, это можно сделать, создав третий класс, но мой любимый подход - создать взаиморекурсивные лямбда-функции, поскольку он позволяет захватывать переменные (следовательно, делает мою жизнь проще в моем реальном случае Применение). Так выглядит:
#include <functional>
#include <iostream>
int main()
{
struct B;
struct A { B * b; };
struct B { A * a; };
std::function< void(A *) > foo;
std::function< void(B *) > bar;
foo = [&] (A * a)
{
std::cout << "calling foo" << std::endl;
if(a->b) { bar(a->b); }
};
bar = [&] (B * b)
{
std::cout << "calling bar" << std::endl;
if(b->a) { foo(b->a); }
};
A a = {0};
B b = {&a};
foo(&a);
bar(&b);
return 0;
}
Это компилирует и печатает:
calling foo
calling bar
calling foo
Обратите внимание, что тип lambdas должен быть указан вручную, поскольку вывод типа не работает с рекурсивными лямбдами.
Внедрить виртуальный A, для использования B, тогда реальный A.
struct virtA
{
virtual void foo() = 0 ;
} ;
struct B
{
virtA * a ;
void bar() { if ( a) { a->foo() ; } }
} ;
struct A : public virtA
{
B * b ;
void bar() { if ( b) { b-> bar() ; } }
} ;