Вопрос о вложенном классе C ++ / форвардном объявлении - PullRequest
32 голосов
/ 08 апреля 2010

Можно ли заранее объявить вложенный класс, а затем использовать его в качестве типа для конкретного (не указателя / ссылки на) члена данных внешнего класса?

И.Е.

class Outer;

class Outer::MaybeThisWay   // Error: Outer is undefined
{
};

class Outer
{
 MaybeThisWay x;

 class MaybeThatOtherWay;

 MaybeThatOtherWay y;   // Error: MaybeThatOtherWay is undefined
};

Ответы [ 6 ]

37 голосов
/ 08 апреля 2010

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

В зависимости от того, что вы пытаетесь сделать, возможно, вы можете использовать пространство имен, а не класс на внешнем слое. Вы можете пересылать-объявлять такой класс без проблем:

namespace Outer {
   struct Inner; 
};

Outer::Inner* sweets;  // Outer::Inner is incomplete so 
                       // I can only make a pointer to it

Если ваш Outer обязательно должен быть классом, и вы не можете вставить его в пространство имен, тогда вам нужно, чтобы Outer был полным типом в контексте, где вы пересылаете объявление Inner.

class Outer
{
   class Inner;  // Inner forward-declared
};  // Outer is fully-defined now

Outer yes;  // Outer is complete, you can make instances of it
Outer::Inner* fun;  // Inner is incomplete, you can only make 
                    // pointers/references to it

class Outer::Inner 
{
};  // now Inner is fully-defined too

Outer::Inner win;  // Now I can make instances of Inner too
15 голосов
/ 08 апреля 2010

Невозможно переслать объявление вложенного класса, не указав полностью содержащий класс. Этот маленький трюк как бы решает проблему, хотя

class Outer_Inner
{
};

class Outer
{
public:
   typedef Outer_Inner Inner;
};

Это работает для меня, как в моем соглашении об именах Outer_Inner не является допустимым именем класса, поэтому очевидно, что оно относится к вложенному классу.

Вы все еще не можете объявить вложенный класс следующим образом:

class Outer::Inner;

Но, по крайней мере, он может быть объявлен следующим образом:

class Outer_Inner;

Если вам не нравится, как выглядит Outer_Inner, вы можете принять соглашение об именах для вложенных классов, которое лучше соответствует вашим вкусам. Outer__Inner, Outer_nested_Inner и т. Д.

1 голос
/ 08 апреля 2010

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

1 голос
/ 08 апреля 2010

Нет, но что не так с

class Outer {
public:  //or protected or private
    class Inner {
    };

private:
    Inner foo;
};

Объявление вперёд здесь не имеет смысла, если только я что-то упустил (что возможно, если в вашем вопросе недостаточно деталей)

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

0 голосов
/ 21 декабря 2015

Если вам просто нужен тип в качестве параметра функции или статической переменной, это может быть сделано на стороне клиента. Например, чтобы получить уведомление о событии от Outer:

Интерфейс:

class Client {
public:
private:
    static void gotIt(int event);
    class Helper;
};

Реализация:

#include <outer.hpp>

class Client::Helper {
public:
    static void fromOuter(Outer::Inner const& inner) 
    { 
        gotIt(inner.event());
    }
};
0 голосов
/ 08 апреля 2010

Если вы объявляете атрибут типа MaybeThatOtherWay, а не ссылку или указатель, компилятор должен знать полное определение класса, чтобы определить размер внешнего класса. Таким образом, вы не можете использовать предварительное объявление и такого рода объявление поля, независимо от того, является ли он вложенным классом или нет.

...