Как создать структуру, которая содержит список самой себя?

StackOverflow https://stackoverflow.com/questions/915445

  •  06-09-2019
  •  | 
  •  

Вопрос

Я хочу создать структуру, которая содержит список такой же структуры, как эта:

#include <list>
struct Url
{
    CString strUrl;
    std::list<Url> children;
};

int main()
{
    Url u1, u2;
    u1.children.push_back(u2);
}

Этот код не компилируется.Но когда я заменяю std::list с std::vector это работает нормально.Как я могу заставить это работать с std::list?

Окно вывода содержит следующую ошибку.

c:\program files\microsoft visual studio\vc98\include\list(29) : error C2079: '_Value' uses undefined struct 'Url'
        E:\test\Test.cpp(23) : see reference to class template instantiation 'std::list<struct Url,class std::allocator<struct Url> >' being compiled
c:\program files\microsoft visual studio\vc98\include\functional(185) : error C2079: 'value' uses undefined struct 'Url'
        c:\program files\microsoft visual studio\vc98\include\list(285) : see reference to class template instantiation 'std::binder2nd<struct std::not_equal_to<struct Url> >' being compiled
        E:\test\Test.cpp(23) : see reference to class template instantiation 'std::list<struct Url,class std::allocator<struct Url> >' being compiled
Это было полезно?

Решение

Если вам нужен обходной путь для того, что кажется ошибкой VC6, создайте список динамически:

#include <list>
#include <string>     // I don't use MFC

struct Url
{
    std::string strUrl;
    std::list<Url> * children;

    Url() {
       children = new std::list <Url>;
    }

    ~Url() {
        delete children;
    }
};

int  main()
{
    Url u1, u2;
    u1.children->push_back(u2);
}

Некоторые спрашивали, почему списки того же типа, что и участники, разрешены (и, на мой взгляд, так оно и есть), когда

Url array[5]; 

например, как участник не был бы.Я тоже ничего не могу найти в стандарте, но sizeof( std:;list <T>) не зависит от того, что это за список.Предположим, list был реализован как (здесь какой-то псевдо C ++):

list <T> {
   listEntry <T> * first;
};

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

template <typename T> struct A {
};

struct B {
    A <B> b;
};

Я не вижу никакой возможной причины, по которой это не должно быть законным.

Другие советы

Можете ли вы сказать нам, какой компилятор вы используете?В том, что вы делаете, нет ничего изначально неправильного.Я попробовал следующее на VS2008 SP1, и оно скомпилировалось без проблем

#include <list>

struct Url
{
    std::string name;
    std::list<Url> children;
};

int _tmain(int argc, _TCHAR* argv[])
{
    Url u1,u2;
    u1.children.push_back(u2);
    return 0;
}

Возможно, вы забыли включить список?

Редактировать

OP использует Visual Studio 6.0, и Нил смог подтвердить, что это действительно ошибка в VS6

Вопреки утверждениям в других ответах, это действительно не законно создавать экземпляры любого стандартного контейнера, включая std::list, с неполным типом.(Для a обсуждение этого см., например Как здесь можно использовать неполный тип в качестве параметра шаблона для vector?)

Это требование смягчается только в C ++ 17 для std::forward_list, std::list и std::vector.Для любого более раннего стандарта исходный код, работающий с более новыми версиями VC и gcc, является нестандартным расширением.Это также относится к вашим наблюдениям с std::vector.

В версии, предшествующей C ++ 17, для переносимости std::list из какого - то класса T как член указанного класса, вам действительно нужен обходной путь, такой как std::list<T*> или используйте библиотеку boost.container, которая уже переносимо реализует смягченные требования.

Обратите внимание, что даже в C ++ 17 вы можете создать экземпляр самого шаблона класса только с неполным типом.Тип по-прежнему должен быть полным при создании экземпляра любого элемента.

Интересно - вы пытаетесь создать vector или list неполного типа.При беглом взгляде на стандарт я не могу найти ничего, что говорило бы о том, разрешено это или нет для типов контейнеров, включенных в стандартную библиотеку C ++.Любое из этих решений казалось бы разумным:

Почему это может быть запрещено: Вы не можете объявить объект типа X внутри определения X.

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

struct X {
    X x;
};

Почему это может быть разрешено: Большинство контейнеров могут изменять размер, что на практике требует определенного уровня косвенности (указателей) на фактические элементы данных.Это законно - объявлять указатель на-X внутри определения X.

Как следует из последнего абзаца, обычный способ обойти эту проблему - использовать указатели или ссылки на X.Например.следующие два фрагмента компилируются просто отлично:

struct Y {
    Y* y;
};

struct Z {
    std::list<Z*> zl;
    std::vector<Z*> zv;
};

Кто-нибудь (ОК, я имею в виду litb :-P) знает, каковы на самом деле требования к стандартным типам контейнеров?

Код отлично компилируется с помощью GCC 4.4 И отлично выполняется.MSVC ++ до версии 7 не был полностью совместим со стандартами.Вам следует рассмотреть возможность использования более нового компилятора.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top