C ++ constante string estática (membro da class)

Eu gostaria de ter uma constante estática privada para uma class (neste caso, uma fábrica de formas). Eu gostaria de ter algo do tipo.

class A { private: static const string RECTANGLE = "rectangle"; } 

Infelizmente eu recebo todos os tipos de erro do compilador C + + (g + +), como:

O ISO C ++ proíbe a boot do membro ‘RECTANGLE’

boot inválida na class de membro de dados estáticos do tipo não integral ‘std :: string’

erro: fazendo estática ‘RETANGULAR’

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 ter que usar uma diretiva #define (eu quero evitar a feia globalidade dos dados!)

Qualquer ajuda é apreciada. Obrigado.

Você precisa definir seu membro estático fora da definição de class e fornecer o inicializador lá.

Primeiro

 // In a header file (if it is in a header file in your case) class A { private: static const string RECTANGLE; }; 

e depois

 // In one of the implementation files const string A::RECTANGLE = "rectangle"; 

A syntax que você estava tentando usar originalmente (inicializador dentro da definição de class) só é permitida com os tipos integral e enum.

Em C ++ 11 você pode fazer agora:

 class A { private: static constexpr const char* STRING = "some useful string constant"; }; 

Dentro de definições de classs, você só pode declarar membros estáticos. Eles precisam ser definidos fora da class. Para constantes integrais em tempo de compilation, o padrão faz a exceção de que você pode “inicializar” membros. Ainda não é uma definição, no entanto. Tomando 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 boot dinâmica. Então, se você escrever algo como

 const std::string foo = "hello"; 

No escopo de espaço de nomes, o construtor de foo será executado logo antes da execução de partidas principais e esse construtor criará uma cópia da constante “hello” na memory heap. A menos que você realmente precise que o RETANGLE seja um std :: string, você poderia muito bem escrever

 // class definition with incomplete static member could be in a header file class A { static const char RECTANGLE[]; }; // this needs to be placed in a single translation unit only const char A::RECTANGLE[] = "rectangle"; 

Lá! Nenhuma alocação de heap, sem cópia, sem boot dinâmica.

Felicidades, s.

Esta é apenas uma informação extra, mas se você realmente quiser a string em um arquivo de header, tente algo como:

 class foo { public: static const std::string& RECTANGLE(void) { static const std::string str = "rectangle"; return str; } }; 

Embora eu duvide que seja recomendado.

Para usar essa syntax de boot em class, a constante deve ser uma constante estática de tipo integral ou de enumeração inicializada por uma expressão constante.

Essa é a restrição. Portanto, nesse caso, você precisa definir uma variável fora da class. consulte answerwer de @AndreyT

Em C ++ 17 você pode usar variables ​​inline :

 class A { private: static inline const std::string my_string = "some useful string constant"; }; 

Note que isto é diferente da resposta do abyss.7 : Este define um object std::string atual, não um const char*

O padrão atual somente permite essa boot para tipos integrais de constante estática. Então você precisa fazer como AndreyT explicou. No entanto, isso estará disponível no próximo padrão por meio da nova syntax de boot do membro .

possível apenas faça:

 static const std::string RECTANGLE() const { return "rectangle"; } 

ou

 #define RECTANGLE "rectangle" 

Você pode ir para a solução const char* mencionada acima, mas se precisar de strings o tempo todo, você terá muita sobrecarga.
Por outro lado, a string estática precisa de boot dinâmica, portanto, se você quiser usar seu valor durante outra boot da variável global / estática, poderá encontrar o problema da ordem de boot. Para evitar isso, o mais barato é acessar o object string estático através de um getter, que verifica se o seu object é inicializado ou não.

 //in a header class A{ static string s; public: static string getS(); }; //in implementation string A::s; namespace{ bool init_A_s(){ A::s = string("foo"); return true; } bool A_s_initialized = init_A_s(); } string A::getS(){ if (!A_s_initialized) A_s_initialized = init_A_s(); return s; } 

Lembre-se de usar apenas A::getS() . Como qualquer encadeamento só pode ser iniciado por main() e A_s_initialized é inicializado antes de main() , você não precisa de bloqueios, mesmo em um ambiente multithread. A_s_initialized é 0 por padrão (antes da boot dinâmica), portanto, se você usar getS() antes de s ser inicializado, chamará a function init com segurança.

Btw, na resposta acima: ” static const std :: string RECTANGLE () const “, funções estáticas não podem ser const porque elas não podem mudar o estado se qualquer object de qualquer maneira (não existe este ponteiro).

As variables ​​estáticas de class podem ser declaradas no header, 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 object gerado será colocado, portanto, é necessário 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 precisa ser definido em um arquivo .cpp, mas os valores estão no header.

 class A { private: static struct _Shapes { const std::string RECTANGLE {"rectangle"}; const std::string CIRCLE {"circle"}; } shape; }; 

Em vez de inicializar membros individuais, toda a estrutura estática é inicializada em .cpp:

 A::_Shapes A::shape; 

Os valores são acessados ​​com

 A::shape.RECTANGLE; 

ou – uma vez que os membros são privados e se destinam a ser usados ​​apenas de A – com

 shape.RECTANGLE; 

Observe que essa solução ainda sofre com o problema da ordem de boot das variables ​​estáticas. Quando um valor estático é usado para inicializar outra variável estática, o primeiro pode não ser inicializado ainda.

 // file.h class File { public: static struct _Extensions { const std::string h{ ".h" }; const std::string hpp{ ".hpp" }; const std::string c{ ".c" }; const std::string cpp{ ".cpp" }; } extension; }; // file.cpp File::_Extensions File::extension; // module.cpp static std::set headers{ File::extension.h, File::extension.hpp }; 

Nesse caso, os headers da variável estática conterão {“”} ou {“.h”, “.hpp”}, dependendo da ordem de boot criada pelo vinculador.

Como mencionado por @ abyss.7 você também pode usar constexpr se o valor da variável puder ser calculado em tempo de compilation. Mas se você declarar suas strings com static constexpr const char* e seu programa usar std::string caso contrário, haverá uma sobrecarga, porque um novo object std::string será criado toda vez que você usar tal constante:

 class A { public: static constexpr const char* STRING = "some value"; }; void foo(const std::string& bar); int main() { foo(A::STRING); // a new std::string is constructed and destroyed. } 

Avanço rápido para 2018 e C ++ 17.

 using namespace std::literals; namespace STANDARD { constexpr inline auto compiletime_static_string_view_constant() { // make and return string view literal // will stay the same for the whole application lifetime // will exhibit standard and expected interface // will be usable at both // runtime and compile time // by value semantics implemented for you auto made_once_when_needed_ = "compile time"sv; return made_once_when_needed_ ; } }; 

Acima está um cidadão C ++ padrão e legal. Ele pode se envolver prontamente em qualquer um e todos os std :: algoritmos, contêineres, utilitários e um tal. Por exemplo:

 // test the resilience auto return_by_val = []() { auto return_by_val = []() { auto return_by_val = []() { auto return_by_val = []() { return STANDARD::compiletime_static_string_view_constant(); }; return return_by_val(); }; return return_by_val(); }; return return_by_val(); }; // actually a run time _ASSERTE(return_by_val() == "compile time"); // compile time static_assert( STANDARD::compiletime_static_string_view_constant() == "compile time" ); 

Aproveite o padrão C ++