Eu gostaria de ter uma constante estática privada para uma classe (neste caso, uma fábrica de formas).
Eu gostaria de ter algo do tipo.
class A {
private:
static const string RECTANGLE = "rectangle";
}
Infelizmente, recebo todos os tipos de erro do compilador C ++ (g ++), como:
O ISO C ++ proíbe a inicialização do membro 'RECTANGLE'
inicialização inválida na classe do membro de dados estáticos do tipo não integral 'std :: string'
erro: tornando 'RECTANGLE' estático
Isso me diz que esse tipo de design de membro não é compatível com o padrão. Como você tem uma constante literal privada (ou talvez pública) sem precisar usar uma diretiva #define (eu quero evitar a feia da globalidade dos dados!)
Qualquer ajuda é apreciada.
Respostas:
Você precisa definir seu membro estático fora da definição de classe e fornecer o inicializador lá.
Primeiro
e depois
A sintaxe que você estava originalmente tentando usar (inicializador dentro da definição de classe) é permitida apenas nos tipos integral e enum.
A partir do C ++ 17, você tem outra opção, que é bastante semelhante à sua declaração original: variáveis inline
Nenhuma definição adicional é necessária.
Ou, em vez de
const
você, pode declararconstexpr
nesta variante. Explícitoinline
não seria mais necessário, poisconstexpr
implicainline
.fonte
char const*
tem a bondade de ser inicializado antes que toda a inicialização dinâmica seja concluída. Portanto, no construtor de qualquer objeto, você pode confiarRECTANGLE
para ter sido inicializado já.No C ++ 11, você pode fazer agora:
fonte
constexpr
implicaconst
para var, não para o tipo que aponta. Ou seja,static constexpr const char* const
é o mesmo questatic constexpr const char*
, mas não é o mesmo questatic constexpr char*
.Definições de classe interna, você só pode declarar membros estáticos. Eles precisam ser definidos fora da classe. Para constantes integrais em tempo de compilação, o padrão faz a exceção de que você pode "inicializar" membros. Ainda não é uma definição, no entanto. Tomar o endereço não funcionaria sem definição, por exemplo.
Eu gostaria de mencionar que não vejo o benefício de usar std :: string sobre const char [] para constantes . std :: string é bom e tudo, mas requer inicialização dinâmica. Então, se você escrever algo como
no escopo do espaço para nome, o construtor foo será executado logo antes da execução das principais partidas e esse construtor criará uma cópia da constante "hello" na memória heap. A menos que você realmente precise de RECTANGLE para ser um std :: string, você também pode escrever
Lá! Sem alocação de heap, sem cópia, sem inicialização dinâmica.
Saúde, s.
fonte
Isso é apenas uma informação extra, mas se você realmente deseja a string em um arquivo de cabeçalho, tente algo como:
Embora eu duvide que seja recomendado.
fonte
No C ++ 17, você pode usar variáveis embutidas :
Observe que isso é diferente da resposta do abyss.7 : Este define um
std::string
objeto real , não umconst char*
fonte
inline
criará muitas duplicatas?Essa é a restrição. Portanto, nesse caso, você precisa definir variáveis fora da classe. consulte resposta de @AndreyT
fonte
As variáveis estáticas da classe podem ser declaradas no cabeçalho, mas devem ser definidas em um arquivo .cpp. Isso ocorre porque pode haver apenas uma instância de uma variável estática e o compilador não pode decidir em qual arquivo de objeto gerado o colocará, portanto, você deve tomar a decisão.
Para manter a definição de um valor estático com a declaração em C ++ 11, uma estrutura estática aninhada pode ser usada. Nesse caso, o membro estático é uma estrutura e deve ser definido em um arquivo .cpp, mas os valores estão no cabeçalho.
Em vez de inicializar membros individuais, toda a estrutura estática é inicializada em .cpp:
Os valores são acessados com
ou - como os membros são privados e devem ser usados apenas em A - com
Observe que essa solução ainda sofre com o problema da ordem de inicialização das variáveis estáticas. Quando um valor estático é usado para inicializar outra variável estática, a primeira ainda não pode ser inicializada.
Nesse caso, os cabeçalhos das variáveis estáticas conterão {""} ou {".h", ".hpp"}, dependendo da ordem de inicialização criada pelo vinculador.
Conforme mencionado por @ abyss.7, você também pode usar
constexpr
se o valor da variável puder ser calculado em tempo de compilação. Mas se você declarar suas strings comstatic constexpr const char*
e seu programa usar destd::string
outra forma, haverá uma sobrecarga porque um novostd::string
objeto será criado toda vez que você usar essa constante:fonte
O padrão atual permite apenas essa inicialização para tipos integrais constantes estáticos. Então você precisa fazer o que o AndreyT explicou. No entanto, isso estará disponível no próximo padrão através da sintaxe de inicialização do novo membro .
fonte
possível apenas faça:
ou
fonte
constexpr
mas não puder criar uma função estáticaconst
.static const std::string RECTANGLE() const { static const std::string value("rectangle"); return value; }
Você pode optar pela
const char*
solução mencionada acima, mas se precisar de strings o tempo todo, terá muita sobrecarga.Por outro lado, a cadeia estática precisa de inicialização dinâmica; portanto, se você quiser usar seu valor durante a inicialização de outra variável global / estática, poderá encontrar o problema da ordem de inicialização. Para evitar isso, o mais barato é acessar o objeto de cadeia estática por meio de um getter, que verifica se o seu objeto foi inicializado ou não.
Lembre-se de usar apenas
A::getS()
. Como qualquer encadeamento só pode ser iniciadomain()
eA_s_initialized
inicializado antesmain()
, você não precisa de bloqueios, mesmo em um ambiente multithread.A_s_initialized
é 0 por padrão (antes da inicialização dinâmica); portanto, se você usargetS()
antes da inicialização de s, chame a função init com segurança.Aliás, na resposta acima: " estática const std :: string RECTANGLE () const ", funções estáticas não podem ser
const
porque não podem alterar o estado de qualquer objeto de qualquer maneira (não existe esse ponteiro).fonte
Avanço rápido para 2018 e C ++ 17.
static_assert 'funciona' somente em tempo de compilação
};
Acima está um cidadão C ++ padrão adequado e legal. Ele pode se envolver prontamente em todo e qualquer algoritmo std ::, contêineres, utilitários e similares. Por exemplo:
Aproveite o C ++ padrão
fonte
std::string_view
para constantes apenas se você usarstring_view
parâmetros em todas as suas funções. Se alguma de suas funções usar umconst std::string&
parâmetro, uma cópia de uma sequência será criada quando você passar umastring_view
constante por esse parâmetro. Se suas constantes forem do tipo,std::string
as cópias não serão criadas nem paraconst std::string&
parâmetros nem parastd::string_view
parâmetros.inline
variáveis chegassem ao C ++ 17 com sua semântica de ODR. Mas string_view é C ++ 17 também, por isso sóconstexpr auto some_str = "compile time"sv;
faz o trabalho (e, na verdade, não é uma variável, éconstexpr
, por isso,inline
está implícito, se você tem uma variável - ou seja, nãoconstexpr
- entãoinline auto some_str = "compile time"sv;
vai fazê-lo, embora, naturalmente, um namespace escopo variável, que é essencialmente uma variável global, raramente seria uma boa ideia).