Como criar uma estrutura que contém uma lista de si?
Pergunta
Eu quero criar uma estrutura que contém uma lista de mesma estrutura como esta:
#include <list>
struct Url
{
CString strUrl;
std::list<Url> children;
};
int main()
{
Url u1, u2;
u1.children.push_back(u2);
}
Este código não está compilando. Mas quando eu substituir std::list
com std::vector
é bom trabalhar. Como posso fazer este trabalho com std::list
?
janela de saída contém o seguinte erro.
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
Solução
Se você precisa de um workround para o que parece ser um bug VC6, criar a lista dinamicamente:
#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);
}
Alguns perguntaram por que listas do mesmo tipo que os membros são permitidos (e na minha opinião, eles são) quando
Url array[5];
por exemplo, como um membro não seria. Eu não consigo encontrar nada no padrão também, mas sizeof( std:;list <T>)
não é dependente da coisa é uma lista de. lista Suponha que foi implementado como (alguns pseudo C ++ aqui):
list <T> {
listEntry <T> * first;
};
então não há tamanho desconhecido para lidar com eles. Considere o seguinte código mínimo que aborda o problema questionadores:
template <typename T> struct A {
};
struct B {
A <B> b;
};
Eu não consigo ver qualquer razão possível que isso não deve ser legal.
Outras dicas
Você pode nos dizer o compilador que você está usando? Não há nada de intrinsecamente errado com o que você está fazendo. Eu tentei o seguinte em VS2008 SP1 e compilado nenhum problema
#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;
}
Você talvez se esqueça de incluir lista?
Editar
OP é usando o Visual Studio 6.0 e Neil foi capaz de confirmar que ele é realmente um bug no VS6
Ao contrário das afirmações nas outras respostas, é de fato não legal para instanciar qualquer recipiente padrão, incluindo std::list
, com um tipo incompleto. (Para um linguagem advogado discussão desta, ver, por exemplo como um tipo incompleto pode ser usado como um parâmetro de modelo para vector aqui? )
Este requisito só se relaxou em C ++ 17 para std::forward_list
, std::list
e std::vector
. Por qualquer padrão anteriormente, o código original trabalhar com versões mais recentes do VC e gcc é uma extensão não-padrão. Isto também se aplica às suas observações com std::vector
.
Na pré-C ++ 17, ter portably um std::list
de algum T
classe como um membro da referida classe, você precisa de uma solução alternativa como std::list<T*>
ou usar a biblioteca boost.container, que já portably implementa os requisitos relaxado.
Note que, mesmo em C ++ 17, você só pode instanciar o próprio modelo de classe com um tipo incompleto. O tipo deve ainda ser completa quando qualquer membro é instanciado.
Interessante - você está tentando criar um vector
ou list
do tipo incompleto. De um rápido olhar para o padrão, eu não consigo encontrar nada dizer se este é ou não é suposto ser permitido para tipos de contêineres incluídos na biblioteca padrão C ++. De qualquer decisão parece ser razoável:
Por que ele não pode ser permitido:. Você não pode declarar um objeto do tipo X
dentro da definição de X
por exemplo. o seguinte código não compilar porque criaria uma estrutura de dados infinitamente profunda:
struct X {
X x;
};
Por que isso pode ser permitido: A maioria dos recipientes são redimensionáveis, necessitando de um nível de engano (ponteiros) aos elementos de dados reais em prática. É legal para declarar um ponteiro-se X
dentro da definição de X
.
Como o último parágrafo sugere, a forma usual de contornar este problema é usar ponteiros ou referências a X
. Por exemplo. os dois trechos seguintes compilar muito bem:
struct Y {
Y* y;
};
struct Z {
std::list<Z*> zl;
std::vector<Z*> zv;
};
Alguém (OK, eu quero dizer litb :-P) saber o que os requisitos realmente são para tipos de contêineres padrão?
O código compila perfeitamente bem com GCC 4.4 E executa perfeitamente. MSVC ++ anteriores à versão 7, não era totalmente compatível com os padrões. Você deve considerar o uso de um compilador mais recente.