C++ статическая постоянная строка (член класса)

Я хотел бы иметь частную статическую константу для класса (в этом случае shape-factory). Мне бы хотелось иметь что-нибудь в этом роде.

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

к сожалению, я получаю всевозможные ошибки от компилятора C++ (g++), например:

ISO C++ запрещает инициализацию член "прямоугольник"

недопустимая инициализация в классе статического элемента данных нецелочисленного типа ' std:: string’

ошибка: создание " прямоугольника’ static

Это говорит мне, что такой дизайн элемента не соответствует стандарту. Как у вас есть частная литеральная константа (или, возможно, публичная) без использования директивы #define (я хочу избежать уродства глобальности данных!)

любая помощь приветствуется. Спасибо.

11 ответов


вы должны определить свой статический член вне определения класса и есть инициализатор.

первый

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

а то

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

синтаксис, который вы изначально пытались использовать (инициализатор внутри определения класса), разрешен только с типами integral и enum.


В C++11 можно сделать так:

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

внутри определений классов вы можете только объявить статические члены. Они должны быть определена вне класса. Для интегральных констант времени компиляции стандарт делает исключение, которое вы можете "инициализировать" члены. Хотя это все еще не определение. Например, взятие адреса не будет работать без определения.

Я хотел бы упомянуть, что я не вижу преимущества использования std:: string над const char []для константы. std:: string хорош и все, но требует динамической инициализации. Итак, если вы пишете что-то вроде

const std::string foo = "hello";

в области пространства имен конструктор foo будет запущен непосредственно перед выполнением основных запусков, и этот конструктор создаст копию константы "hello" в памяти кучи. Если вам действительно не нужен прямоугольник, чтобы быть строкой std::, вы могли бы также написать

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

там! Нет выделения кучи, нет копирования, нет динамики инициализация.

Ура, ы.


Это просто дополнительная информация, но если вы действительно хотите строку в файле заголовка, попробуйте что-то вроде:

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

        return str;
    }
};

хотя я сомневаюсь, что это рекомендуется.


использовать эту инициализацию в классе синтаксис, константа должна быть статической const интегрального или перечислительного типа инициализирована константным выражением.

это ограничение. Следовательно, в этом случае вам нужно определить переменную вне класса. обратитесь к ответчику от @AndreyT


В C++ 17 вы можете использовать встроенные переменные:

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

обратите внимание, что это отличается от пропасть.Ответ 7-х: этот определяет фактический


текущий стандарт допускает такую инициализацию только для статических постоянных интегральных типов. Поэтому вам нужно сделать так, как объяснил Андрейт. Тем не менее, это будет доступно в следующем стандарте через синтаксис инициализации нового члена.


можно просто сделать:

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

или

#define RECTANGLE "rectangle"

вы можете либо пойти на const char* решение, упомянутое выше, но тогда, если вам нужна строка все время, у вас будет много накладных расходов.
С другой стороны, статическая строка нуждается в динамической инициализации, поэтому, если вы хотите использовать ее значение при инициализации другой глобальной/статической переменной, вы можете столкнуться с проблемой порядка инициализации. Чтобы избежать этого, самая дешевая вещь-доступ к статическому строковому объекту через геттер, который проверяет, инициализирован ли ваш объект или нет.

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

не забудьте использовать только A::getS(). Потому что любая резьба может начинаться только с main() и A_s_initialized инициализируется перед main(), вам не нужны замки даже в многопоточной среде. A_s_initialized по умолчанию-0 (до динамической инициализации), поэтому если вы используете getS() перед инициализацией s вы вызываете функцию init безопасно.

кстати, в ответе выше:"static const std:: string RECTANGLE () const" , статические функции не может быть!--8--> потому что они не могут изменить состояние, если какой-либо объект в любом случае (нет этого указателя).


статические переменные класса могут быть объявил в заголовке, но должны быть определена в a .файл cpp. Это связано с тем, что может быть только один экземпляр статической переменной, и компилятор не может решить, в какой сгенерированный объектный файл поместить его, поэтому вам нужно принять решение.

чтобы сохранить определение статического значения с объявлением в C++11 можно использовать вложенную статическую структуру. В этом случае статический элемент структура и должен быть определен в .cpp файл, но значения в заголовке.

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

вместо инициализации отдельных элементов инициализируется вся статическая структура .cpp:

A::_Shapes A::shape;

значения осуществляется с

A::shape.RECTANGLE;

или -- так как члены являются частными и предназначены для использования только из-с

shape.RECTANGLE;

обратите внимание, что это решение по-прежнему страдает от проблемы порядка инициализация статического переменная. Когда статическое значение используется для инициализируйте другую статическую переменную, первая не может быть инициализирована, еще.

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

в этом случае статическая переменная заголовки будет содержать либо { "" } или." { ч." ,"hpp"}, в зависимости от порядка инициализации, созданного компоновщиком.

как упоминалось @abyss.7 вы также можете использовать constexpr если значение переменной может быть вычислено во время компиляции. Но если вы объявите свои строки с static constexpr const char* и ваша программа использует std::string в противном случае будут накладные расходы, потому что новый


быстрая перемотка вперед к 2018 и 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_  ;
    }
};

выше является правильным и юридическим стандартом c++ citizen. Он может легко участвовать в любых и всех алгоритмах std::, контейнерах, утилитах и т. д. Например:

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

наслаждайтесь стандартным C++