Pergunta

Eu gostaria de definir um char * constante no meu arquivo de cabeçalho para o meu arquivo cpp para uso. Então, eu tentei isso:

private:
    static const char *SOMETHING = "sommething";

O que me leva com o seguinte erro do compilador:

Erro C2864: 'SomeClass :: ALGO': Dados integrante const única estática membros podem ser inicializados dentro de um classe

Eu sou novo para C ++. O que está acontecendo aqui? Por que isso é ilegal? E como você pode fazê-lo em alternativa?

Foi útil?

Solução

Você precisa definir variáveis ??estáticas em uma unidade de tradução, a menos que eles são de tipos integrais.

Em seu cabeçalho:

private:
    static const char *SOMETHING;
    static const int MyInt = 8; // would be ok

No arquivo .cpp:

const char *YourClass::SOMETHING = "something";

C ++ padrão, 9.4.2 / 4:

Se um membro de dados estáticos é de const tipo de enumeração integrante ou const, sua declaração na classe definição pode especificar um constante-initializer que será um expressão constante integrante. Naquilo caso, o membro pode aparecer em expressões constantes integrais dentro seu escopo. O membro deve ainda ser definido em um escopo namespace se é usado no programa eo namespace definição do escopo não deve conter uma initializer.

Outras dicas

Para responder à pergunta do OP sobre o motivo que só é permitido com tipos integrais.

Quando um objeto é usado como um lvalue (ou seja, como algo que tem um endereço em armazenamento), tem que satisfazer a "regra de uma definição" (ODR), ou seja, tem de ser definido em uma e apenas uma unidade de tradução. O compilador não pode e não irá decidir qual a unidade de tradução para definir esse objeto. Esta é sua responsabilidade. Por definição que objeto em algum lugar você não está apenas definindo-o, na verdade você está dizendo ao compilador que você quer para defini-lo aqui , em este específica tradução unidade.

Enquanto isso, em linguagem C ++ constantes integral têm um estatuto especial. Eles podem formar expressões constantes integrais (CIEM). Em ICEs constantes integral são usados ??como normais valores , e não como objetos (ou seja, não é necessário, se essa valor integral tem endereço no armazenamento ou não). Na verdade, o CIEM são avaliadas em tempo de compilação. A fim de facilitar o uso de tal de constantes integrais seus valores têm de ser globalmente visível. E a própria constante realmente não precisa de um lugar real no armazenamento. Devido a isso constantes integral recebeu tratamento especial:. Foi permitida a inclusão de seus initializers no arquivo de cabeçalho, e a exigência de fornecer uma definição foi relaxado (primeiro de facto, depois de jure)

Outros tipos constantes não tem essas propriedades. Outros tipos constantes são quase sempre utilizado como lvalues ??(ou pelo menos não pode participar de MCI ou qualquer coisa semelhante ao ICE), o que significa que eles exigem uma definição. O resto segue.

O erro é que você não pode inicializar uma static const char* dentro da classe. Você só pode inicializar inteiro variáveis ??lá.

Você precisa declarar a variável de membro na classe, e em seguida, inicialize-o fora da classe:

// arquivo de cabeçalho

class Foo {
    static const char *SOMETHING;
    // rest of class
};

// cpp

const char *Foo::SOMETHING = "sommething";

Se isso parece chato, pense nisso como sendo porque a inicialização só pode aparecer em uma unidade de tradução. Se fosse na definição da classe, que normalmente seriam incluídos por vários arquivos. inteiros constantes são um caso especial (que significa a mensagem de erro, talvez, não é tão claro como poderia ser), e compiladores pode efetivamente substituir usos da variável com o valor inteiro.

Em contraste, uma variável char* aponta para um objeto real na memória, o que é necessário para realmente existe, e é a definição (incluindo a inicialização), que faz com que a exist objeto. Os meios "regra de uma definição", portanto, você não quer colocá-lo em um cabeçalho, porque então todas as unidades de tradução incluindo o cabeçalho deve conter a definição. Eles não podiam ser ligados entre si, mesmo que a cadeia contém os mesmos personagens em ambos, porque sob as regras atuais de C ++ que você definiu dois objetos diferentes com o mesmo nome, e isso não é legal. O fato de que eles acontecem ter os mesmos caracteres em si não torná-lo legal.

Com C ++ 11 você pode usar a palavra-chave constexpr e escrever em seu cabeçalho:

private:
    static constexpr const char* SOMETHING = "something";


Notas:

  • constexpr marcas SOMETHING um ponteiro constante para que você não pode escrever

    SOMETHING = "something different";
    

    mais tarde.

  • Dependendo do seu compilador, você pode também precisa escrever uma definição explícita no arquivo .cpp:

    constexpr const char* MyClass::SOMETHING;
    
class A{
public:
   static const char* SOMETHING() { return "something"; }
};

Eu faço isso o tempo todo -. Especialmente para parâmetros padrão caro const

class A{
   static
   const expensive_to_construct&
   default_expensive_to_construct(){
      static const expensive_to_construct xp2c(whatever is needed);
      return xp2c;
   }
};

Se você estiver usando Visual C ++, você pode não portably fazer isso usando dicas para o vinculador ...

// In foo.h...

class Foo
{
public:
   static const char *Bar;
};

// Still in foo.h; doesn't need to be in a .cpp file...

__declspec(selectany)
const char *Foo::Bar = "Blah";

meios __declspec(selectany) que mesmo que Foo::Bar vai ser declarados em vários arquivos de objeto, o vinculador só pegar um.

Tenha em mente isso só funcionará com o conjunto de ferramentas Microsoft. Não espere que isso seja portátil.

Há um truque que você pode usar com modelos para fornecer arquivo H apenas constantes.

(nota, este é um exemplo feio, mas funciona verbatim em pelo menos em g ++ 4.6.1.)

(arquivo values.hpp)

#include <string>

template<int dummy>
class tValues
{
public:
   static const char* myValue;
};

template <int dummy> const char* tValues<dummy>::myValue = "This is a value";

typedef tValues<0> Values;

std::string otherCompUnit(); // test from other compilation unit

(main.cpp)

#include <iostream>
#include "values.hpp"

int main()
{
   std::cout << "from main: " << Values::myValue << std::endl;
   std::cout << "from other: " << otherCompUnit() << std::endl;
}

(other.cpp)

#include "values.hpp"

std::string otherCompUnit () {
   return std::string(Values::myValue);
}

compilação (por exemplo, g ++ -o Main.cpp principal other.cpp && ./main) e ver duas unidades de compilação referenciando a mesma constante declarado num cabeçalho:

from main: This is a value
from other: This is a value

Em MSVC, você pode em vez disso ser capaz de usar __declspec (selectany)

Por exemplo:

__declspec(selectany) const char* data = "My data";

O inicializador constante permitido pelo C ++ padrão apenas para tipos integrais ou enumeração. Veja 9.4.2 / 4 para mais detalhes:

Se um membro de dados estáticos é integrante const ou tipo de enumeração const, sua declaração na classe definição pode especificar uma constante de inicializador que deve ser uma expressão constante integrante (5,19). Naquilo caso, o membro pode aparecer em expressões constantes integrais. O membro deve ainda ser definidos em um nome- escopo espaço, se ele é usado no programa e a definição do escopo namespace não deve conter um inicializador.

E 9.4.2 / 7:

membros de dados estáticos são inicializados e destruiu exatamente como objetos não-locais (3.6.2, 3.6.3).

Assim que você deve escrever em algum lugar no arquivo cpp:

const char* SomeClass::SOMETHING = "sommething";

Para responder à por questão, tipos integrais são especiais em que eles não são uma referência a um objeto alocado, mas sim valores que são duplicados e copiados. é apenas uma decisão de execução tornou quando o idioma foi definida, que era para valores identificador fora do sistema objeto e em como "inline" eficiente e uma maneira possível.

Isto não exatamente explicar por que eles são permitidos como initializors em um tipo, mas pense nisso como essencialmente um #define e, em seguida, ele vai fazer sentido como parte do tipo e não faz parte do objeto.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top