Как продвинуть объявление внутреннего класса?

У меня такой класс ...

class Container {
public:
    class Iterator {
        ...
    };

    ...
};

В другом месте я хочу передать Container :: Iterator по ссылке, но не хочу включать файл заголовка. Если я попытаюсь переслать объявление класса, я получаю ошибки компиляции.

class Container::Iterator;

class Foo {
    void Read(Container::Iterator& it);
};

Компиляция приведенного выше кода дает ...

test.h:3: error: ‘Iterator’ in class ‘Container’ does not name a type
test.h:5: error: variable or field ‘Foo’ declared void
test.h:5: error: incomplete type ‘Container’ used in nested name specifier
test.h:5: error: ‘it’ was not declared in this scope

Как я могу пересылать объявление этого класса, чтобы мне не нужно было включать файл заголовка, который объявляет класс Iterator?


person bradtgmurray    schedule 20.06.2009    source источник


Ответы (3)


Это просто невозможно. Вы не можете пересылать объявление вложенной структуры за пределы контейнера. Вы можете только пересылать объявление в контейнере.

Вам нужно будет сделать одно из следующих

  • Сделайте класс невложенным
  • Измените порядок объявления так, чтобы сначала был полностью определен вложенный класс
  • Создайте общий базовый класс, который может использоваться как в функции, так и реализовываться вложенным классом.
person JaredPar    schedule 20.06.2009
comment
Общий базовый класс - это наиболее часто используемое мной решение. - person Coyote; 03.10.2013
comment
Вы можете использовать друга, чтобы обойти это, если хотите. - person Erik Aronesty; 14.06.2016
comment
Это неправильно: en.cppreference.com/w/cpp/language/nested_types - person Nikerboker; 07.07.2017
comment
@Nikerboker они говорят, что вложенные классы не могут быть объявлены вперед вне контейнера. Пример в ссылке - прямое объявление вложенного класса внутри определения контейнера, что представляет собой другой сценарий. - person Antonio Barreto; 02.01.2018
comment
Тот факт, что невозможно переадресовать объявление вложенной структуры за пределы контейнера, кажется ограничением C ++, и это должно быть возможным. Правильно? Есть ли причина, по которой это невозможно? - person HelloGoodbye; 04.01.2019
comment
@HelloGoodbye Я думаю, что модификаторы доступности - это ограничение: вы не знаете, где их разместить (и следует ли вам это разрешить). - person Wolf; 12.06.2019
comment
@Wolf В этом есть смысл. Разве нельзя было бы указать это, включив модификаторы в выражение прямого объявления? Например. class Container::(public Iterator) или что-то в этом роде? (Я говорю не о том, как сегодня создается язык, а о том, как его теоретически можно разработать.) - person HelloGoodbye; 12.06.2019
comment
@HelloGoodbye У меня такое ощущение, что встраиваемые объявления в классы переоценены. Это несопоставимо с объявлениями и определениями в пространствах имен, даже если доступ через :: выглядит аналогичным. Возможно, случаи, когда программисты хотят, чтобы им было разрешено пересылать объявление класса внутри класса, просто возникают из-за плохого дизайна. C ++ как язык уже очень богат, и его комбинаторика кажется подавляющей и для дизайнеров ... Я не знаю, было бы хорошей идеей надеяться, что это будет оценено как добавляемая функция. - person Wolf; 12.06.2019
comment
@Wolf Поздно к вечеринке, но: Класс, помимо прочего, также является пространством имен, а для объявлений типов и статических членов я думал, что они в точности сопоставимы с точки зрения функциональной эквивалентности. - person Peter - Reinstate Monica; 05.12.2019
comment
@ Peter-ReinstateMonica В C ++ классы закрыты, тогда как пространства имен открыты, и это кажется важным отличием. Для сравнения, в C # есть концепция частичных классов. Я не знаю, планируется ли что-то подобное для следующих стандартов C ++ или нам следует на это надеяться;) - person Wolf; 05.12.2019

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

class Container {
public:
    class Iterator;
};

Затем в отдельном заголовке реализуйте Container :: Iterator:

class Container::Iterator {
};

Затем # включите только заголовок контейнера (или не беспокойтесь о прямом объявлении и просто включите оба)

person Todd Gardner    schedule 20.06.2009
comment
Хороший ответ, за исключением части в скобках в первом абзаце. Невозможно узнать, существует ли на самом деле внутренний класс, не имеет смысла в этом контексте и сомнительно, чтобы быть точным. Весь смысл прямого объявления в том, что вы сообщаете компилятору, что существует класс (или, в данном случае, внутренний класс). Это конкретное ваше утверждение было бы так же верно для обычных классов и означало бы, что вы не можете пересылать объявление чего-либо. - person Loduwijk; 10.11.2016

Я не знаю, как сделать именно то, что вы хотите, но вот обходной путь, если вы хотите использовать шаблоны:

// Foo.h  
struct Foo
{
   export template<class T> void Read(T it);
};

// Foo.cpp
#include "Foo.h"
#include "Container.h"
/*
struct Container
{
    struct Inner { };
};
*/
export template<> 
  void Foo::Read<Container::Inner>(Container::Inner& it)
{

}

#include "Foo.h"
int main()
{
  Foo f;
  Container::Inner i;
  f.Read(i);  // ok
  f.Read(3);  // error
}

Надеюсь, эта идиома может быть вам полезна (и, надеюсь, ваш компилятор основан на EDG и реализует экспорт;)).

person Faisal Vali    schedule 20.06.2009