Por que não posso declarar uma classe em um espaço para nome usando dois pontos duplos?


164
class Namespace::Class;

Por que eu tenho que fazer isso ?:

namespace Namespace {
    class Class;
}

Usando o VC ++ 8.0, o compilador emite:

erro C2653: 'Namespace': não é um nome de classe ou namespace

Presumo que o problema aqui é que o compilador não pode dizer se Namespaceé uma classe ou um espaço para nome? Mas por que isso importa, pois é apenas uma declaração direta?

Existe outra maneira de declarar adiante uma classe definida em algum espaço para nome? A sintaxe acima parece que estou "reabrindo" o espaço para nome e estendendo sua definição. E se Classnão foram realmente definidos Namespace? Isso resultaria em um erro em algum momento?


44
Deixe-me discordar de todas as respostas aqui e dizer que é apenas um bug de design da linguagem. Eles poderiam pensar melhor.
Pavel Radzivilovsky

Isso tende a discutir por que isso é ilegal em C ++ (que é subjetivo) e parece argumentativo. Votação para fechar.
David Thornley

7
Como é o compilador deveria saber que em A::Ba Aé um identificador namespace em vez de um nome de classe?
David R Tribble

@STingRaySC: A discussão é subjetiva, pois não há uma resposta clara por que o C ++ faz isso, então estamos especulando. (A questão é uma espingarda, com algumas perguntas com respostas objetivas, que já foram respondidas.) Nesse ponto, eu me torno sensível a traços de argumento, e seu acordo com Pavel de que isso é um erro de C ++ é qualificado. Eu não teria nenhum problema com a questão de por que importa se Namespaceé uma classe ou um espaço para nome. Apenas não chegue nem perto da sugestão de uma possibilidade concebível de iniciar uma guerra de linguagem por causa da sintaxe.
David Thornley

Respostas:


85

Porque você não pode. Na linguagem C ++, nomes totalmente qualificados são usados ​​apenas para se referir a entidades existentes (isto é, declaradas anteriormente). Eles não podem ser usados ​​para introduzir novas entidades.

E você está de fato "reabrindo" o espaço para nome para declarar novas entidades. Se a classe Classfor definida posteriormente como membro de um espaço para nome diferente - é uma classe completamente diferente que não tem nada a ver com a que você declarou aqui.

Quando você chega ao ponto de definir a classe pré-declarada, não precisa "reabrir" o espaço para nome novamente. Você pode defini-lo no espaço para nome global (ou qualquer espaço para nome que inclua o seu Namespace) como

class Namespace::Class {
  /* whatever */
};

Como você está se referindo a uma entidade que já foi declarada no espaço para nome Namespace, você pode usar o nome qualificado Namespace::Class.


10
@STingRaySC: A única maneira de declarar antecipadamente uma classe aninhada é colocar a declaração dentro da definição da classe envolvente. E, de fato, não há como declarar a classe aninhada antes da definição da classe envolvente.
AnT

@STingRaySC: Uma classe aninhada pode ser declarada - veja minha resposta.
John Dibling

8
@ John Dibling: classe aninhada é uma classe declarada dentro de outra classe. Uma classe declarada imediatamente dentro de um espaço para nome não é uma classe aninhada. Não há nada sobre aulas sensíveis em sua resposta.
AnT

198

Você está obtendo respostas corretas, deixe-me tentar reformular:

class Namespace::Class;

Por que eu tenho que fazer isso?

Você precisa fazer isso porque o termo Namespace::Classestá dizendo ao compilador:

... OK, compilador. Vá, encontre o espaço para nome denominado Namespace e, dentro dele, consulte a classe denominada Class.

Mas o compilador não sabe do que está falando, porque não conhece nenhum espaço para nome nomeado Namespace. Mesmo se houvesse um espaço para nome nomeado Namespace, como em:

namespace Namespace
{
};

class Namespace::Class;

ainda não funcionaria, porque você não pode declarar uma classe dentro de um espaço para nome de fora desse espaço para nome. Você precisa estar no espaço para nome.

Portanto, você pode, de fato, encaminhar declarar uma classe dentro de um espaço para nome. Apenas faça o seguinte:

namespace Namespace
{
    class Class;
};

39
Todas as outras respostas foram confusas para mim, mas esta "você não pode declarar uma classe dentro de um espaço para nome fora desse espaço para nome. Você deve estar no espaço para nome". foi uma dica muito útil para lembrar.
dashesy

22

Suponho que seja pelo mesmo motivo que você não pode declarar namespaces aninhados de uma só vez, como este:

namespace Company::Communications::Sockets {
}

e você tem que fazer isso:

namespace Company {
  namespace Communications {
    namespace Sockets {
    }
  }
}

1
Esta não é realmente uma resposta que explica por que você não pode fazê-lo.
StarPilot 15/07/16

6
Esta é uma resposta que economizou muito tempo
Kadir Erdem Demir 11/10

17
O C ++ 17 adiciona isso.
Rparolin

Aqui você sabe que todos são espaços para nome. Mas com a classe Company :: Communications :: Socket, você não sabe se o Communications é um namespace ou uma classe (onde socket é a classe aninhada).
Lothar

12

Não ficaria claro o que realmente é o tipo de uma variável declarada a frente. A declaração de encaminhamento class Namespace::Class;pode significar

namespace Namespace {
  class Class;
}

ou

class Namespace {
public:
  class Class;
};


6
Eu acho que essa é uma das melhores respostas, porque responde por que isso não pode ser facilmente determinado pelo próprio compilador.
Devolus

1

Há muitas respostas excelentes sobre o raciocínio envolvido em impedi-lo. Eu só quero fornecer a cláusula padrão chata que a proíbe especificamente. Isso vale para C ++ 17 (n4659).

O parágrafo em questão é [class.name] / 2 :

Uma declaração consistindo apenas em identificador de chave de classe ; é uma redeclaração do nome no escopo atual ou uma declaração de encaminhamento do identificador como um nome de classe. Introduz o nome da classe no escopo atual.

O acima define o que constitui uma declaração direta (ou redefinição de uma classe). Essencialmente, deve ser um de class identifier;, struct identifier;ou union identifier;onde identifer é a definição lexical comum em [lex.name] :

identifier:
  identifier-nondigit
  identifier identifier-nondigit
  identifier digit
identifier-nondigit:
  nondigit
  universal-character-name
nondigit: one of
  a b c d e f g h i j k l m
  n o p q r s t u v w x y z
  A B C D E F G H I J K L M
  N O P Q R S T U V W X Y Z _
digit: one of
  0 1 2 3 4 5 6 7 8 9

Qual é a produção do esquema comum com o [a-zA-Z_][a-zA-Z0-9_]*qual todos estamos familiarizados. Como você pode ver, isso impede que class foo::bar;seja uma declaração direta válida, porque foo::barnão é um identificador. É um nome totalmente qualificado, algo diferente.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.