C++ static constant string (CLASS member)

Chciałbym mieć prywatną stałą statyczną dla klasy (w tym przypadku shape-factory). Chciałbym mieć coś takiego.

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

Niestety dostaję różnego rodzaju błędy z kompilatora C++ (g++), takie jak:

ISO C++ zabrania inicjalizacji member 'RECTANGLE'

Nieprawidłowa inicjalizacja w klasie statycznego elementu danych nieintegralnego typu 'std:: string'

Error: making 'RECTANGLE' static

To mi mówi, że tego rodzaju konstrukcja prętów nie jest zgodna ze standardem. Jak można mieć prywatną stałą literalną (a może publiczną) bez konieczności używania dyrektywy #define (chcę uniknąć brzydoty globalności danych!)

Każda pomoc jest mile widziana. Dzięki.
Author: lb., 2009-10-14

11 answers

Musisz zdefiniować swojego statycznego członka poza definicją klasy i podać tam inicjalizator.

Pierwszy

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

A następnie

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

Składnia, której pierwotnie próbowałeś użyć (inicjalizacja wewnątrz definicji klasy) jest dozwolona tylko dla typów integral i enum.

 402
Author: AnT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-02 06:30:06

W C++11 możesz zrobić teraz:

class A {
 private:
  static constexpr const char* STRING = "some useful string constant";
};
 131
Author: abyss.7,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-21 11:27:54

Wewnątrz definicji klas można tylko zadeklarować statyczne elementy. Muszą być zdefiniowane poza klasą. Dla stałych całkowych w czasie kompilacji standard czyni wyjątek, że można "inicjalizować" elementy składowe. To wciąż nie jest definicja. Przyjęcie adresu nie zadziałałoby na przykład bez definicji.

Chciałbym wspomnieć, że nie widzę korzyści z używania std:: string over const char [] dla stałych . std:: string is nice and all but wymaga dynamicznej inicjalizacji. Więc jeśli napiszesz coś w stylu

const std::string foo = "hello";

W obszarze przestrzeni nazw konstruktor foo zostanie uruchomiony tuż przed uruchomieniem main i ten konstruktor utworzy kopię stałej "hello" w pamięci sterty. Chyba, że naprawdę potrzebujesz RECTANGLE aby być std:: string możesz równie dobrze napisać

// 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";
Tam! Bez alokacji sterty, bez kopiowania, bez dynamicznej inicjalizacji. Pozdrawiam, s.
 31
Author: sellibitze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-14 08:30:25

To tylko dodatkowe informacje, ale jeśli naprawdę chcesz, aby ciąg był w pliku nagłówkowym, spróbuj czegoś takiego:

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

        return str;
    }
};
Chociaż wątpię, żeby to było zalecane.
 15
Author: GManNickG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-14 02:08:58

Aby użyć inicjalizacji w klasie składni, stała musi być statyczna const typu całkowego lub wyliczeniowego inicjowane wyrażeniem stałym.

To jest ograniczenie. Dlatego w tym przypadku musisz zdefiniować zmienną poza klasą. odsyłacz od @ AndreyT

 7
Author: aJ.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-14 02:07:11

W C++ 17 możesz użyć zmiennych inline :

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

Zauważ, że to różni się od otchłani.Odpowiedź 7 : ta definiuje rzeczywisty std::string obiekt, a nie const char*

 7
Author: Oz Solomon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-02 15:15:39

Bieżący standard pozwala na taką inicjalizację tylko dla statycznych stałych typów całkowych. Więc musisz zrobić to, co wyjaśnił AndreyT. Jednak będzie to dostępne w następnym standardzie poprzez składnię inicjalizacji new member .

 4
Author: Leandro T. C. Melo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-10-14 02:11:01

Possible just do:

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

Lub

#define RECTANGLE "rectangle"
 4
Author: chikuba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-04-09 09:49:04

Możesz wybrać rozwiązanie const char* wymienione powyżej, ale jeśli potrzebujesz ciągów przez cały czas, będziesz miał dużo kosztów.
Z drugiej strony, statyczny ciąg znaków wymaga dynamicznej inicjalizacji, więc jeśli chcesz użyć jego wartości podczas inicjalizacji innej zmiennej globalnej / statycznej, możesz napotkać problem kolejności inicjalizacji. Aby tego uniknąć, najtańszą rzeczą jest dostęp do statycznego obiektu string poprzez getter, który sprawdza, czy obiekt jest zainicjowany, czy nie.

//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;  
}  

Pamiętaj, aby używać tylko A::getS(). Ponieważ wątek może być uruchamiany tylko przez main(), a A_s_initialized jest inicjowany przed main(), nie potrzebujesz blokad nawet w środowisku wielowątkowym. A_s_initialized domyślnie wynosi 0 (przed dynamiczną inicjalizacją), więc jeśli użyjesz getS() przed inicjalizacją s, wywołasz funkcję init bezpiecznie.

Btw, w odpowiedzi powyżej: "static const std:: string RECTANGLE () const ", funkcje statyczne nie mogą być const, ponieważ nie mogą zmienić stan, jeśli jakikolwiek obiekt i tak (nie ma tego wskaźnika).

 3
Author: user2806882,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-23 11:45:32

Zmienne statyczne klasy mogą być zadeklarowane w nagłówku, ale muszą być zdefiniowane W A .plik cpp. Dzieje się tak dlatego, że może być tylko jedna instancja zmiennej statycznej i kompilator nie może zdecydować, w którym wygenerowanym pliku obiektowym ją umieścić, więc musisz podjąć decyzję, zamiast tego.

Aby zachować definicję wartości statycznej z deklaracją w C++11 można użyć zagnieżdżonej statycznej struktury. W tym przypadku element statyczny jest strukturą i musi być zdefiniowane w a .plik cpp, ale wartości są w nagłówku.

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

Zamiast inicjalizacji poszczególnych członów inicjalizowana jest cała statyczna struktura .cpp:

A::_Shapes A::shape;

Wartości są dostępne za pomocą

A::shape.RECTANGLE;

Or -- since the members are private and are mean to be used only from a -- with

shape.RECTANGLE;

Zauważ, że to rozwiązanie nadal cierpi na problem kolejności inicjalizacja zmiennych statycznych. Gdy wartość statyczna jest używana do initialize inną zmienną statyczną, pierwsza może nie być zainicjalizowana, jeszcze.

// 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<std::string> headers{ File::extension.h, File::extension.hpp };

W tym przypadku zmienna statyczna nagłówki będzie zawierać albo { "" } lub { ".h",".hpp"}, w zależności od kolejności inicjalizacji utworzonej przez linkera.

Jak wspomniano przez @ abyss.7 Możesz również użyć constexpr, Jeśli wartość zmiennej może być obliczona w czasie kompilacji. Ale jeśli zadeklarujesz swoje ciągi za pomocą static constexpr const char* i twój program użyje std::string w przeciwnym razie będzie overhead, ponieważ nowy std::string obiekt będzie tworzony za każdym razem, gdy użyjesz takiej stałej:

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.
}
 3
Author: Marko Mahnič,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-19 09:05:36

Przewiń do przodu do 2018 I 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_  ;
    }
};

Powyżej jest odpowiedni i legalny standard C++ citizen. Może łatwo zaangażować się w dowolne algorytmy std::, kontenery, narzędzia itp. Na przykład:

// 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" 
 );

Enjoy the standard C++

 0
Author: Chef Gladiator,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-20 18:42:04