Правильный способ определить постоянную C-строку в С++?

Большую часть времени я вижу постоянные C-строки, определенные как:

static char const* MY_CONSTANT = "Hello World";

Однако сам указатель не является const. Не было бы более уместно сделать это, как показано ниже?

static char const* const MY_CONSTANT = "Hello World";

Я думаю, что есть 2 цели с постоянными глобальными переменными:

  1. Не разрешать изменение строки
  2. Не позволяйте переменной указывать на что-либо еще

Я просто предположил, что эти две цели были необходимы при определении константных строк.

Еще одна интересная вещь заключается в том, что мне разрешено делать это:

int main()
{
    auto MY_CONSTANT = "";
    MY_CONSTANT = "Another String";
}

Это говорит мне, что auto выводит строку как char const*, а не char const* const.

Итак, у меня два основных вопроса:

  1. Каков наиболее подходящий способ определить постоянные строки в стиле c (я полагаю, постоянные указатели на что-то, это более общий вопрос?). Почему вы выбираете то или иное?
  2. Что касается моего примера с auto, понятно, почему он выбирает char const* (потому что это массив данных, который является константой, а не сам указатель). Могу ли я заставить auto вывести char const* const или изменить код, чтобы он приводил к такому типу?

person void.pointer    schedule 30.09.2014    source источник
comment
Вы имеете в виду const char* const?   -  person Adam    schedule 30.09.2014
comment
@Adam Обе формы действительны C++. Ваш делает то же самое, что и мой.   -  person void.pointer    schedule 30.09.2014
comment
значение по умолчанию, которое обнаруживает auto, является минимальным ограничением, а это означает, что тот факт, что переменная не может быть переназначена, является только вашим выбором. вы можете сделать auto const я полагаю, если хотите, но лучше не навязывать это всем.   -  person v.oddou    schedule 30.09.2014
comment
Строковый литерал может быть связан с const char *; хотите ли вы, чтобы строковая переменная была постоянной, зависит от вас. Вы задаете тот же вопрос, что и should I use const int i=0 or int i=0   -  person texasbruce    schedule 30.09.2014
comment
Я думаю, что OP намекает на тот факт, что существует много кода, который забывает использовать const верхнего уровня, когда предполагалось, что указатель не будет изменен. Если вам нужно программировать во встроенной среде, где это имеет значение для того, сколько статических данных у вас есть, вы довольно быстро научитесь вставлять дополнительные const!   -  person M.M    schedule 30.09.2014
comment
Еще один сорт, о котором вы не упомянули, это char const s[] = "Hello, World!"; . (В C++ ключевое слово static является избыточным, потому что пространство имен const по умолчанию является статическим, хотя в любом случае практично использовать его на случай, если кто-то перенесет ваш заголовок на C и не осознает этого).   -  person M.M    schedule 30.09.2014
comment
Ни static char const* MY_CONSTANT = "Hello World";, ни static char const* const MY_CONSTANT = "Hello World"; не являются более правильными, чем другие. Они означают разные вещи. Используйте тот, который больше подходит для ваших текущих потребностей.   -  person Keith Thompson    schedule 30.09.2014


Ответы (4)


Ну, если это действительно константа, тогда constexpr будет способом C++11 сделать это:

constexpr char const* MY_CONSTANT = "Hello World";

Первый пример:

static char const* MY_CONSTANT = "Hello World";

просто говорит, что у меня есть указатель на константу char со статической продолжительностью хранения, которая, если она находится вне функции, сделает ее глобальной.

Если нам требуется, чтобы указатель также был константным, нам нужна вторая введенная вами форма. Все зависит от того, действительно ли указатель должен быть константным или нет. В большинстве случаев причина, по которой вы видите код без константы верхнего уровня, заключается в том, что они просто забыли вставить ее, а не потому, что они не имели в виду, что указатель также должен быть константой.

Где static имеет значение, например, если вы хотите, чтобы член const был для экземпляра или для класса:

class A
{
        char const* const const_per_instance = "Other Const String";
    public:
        constexpr static char const* const const_per_class = "Hello World" ;
};

Если мы требуем, чтобы константа была для каждого класса, нам нужно использовать static, иначе нет. Пример немного изменится, если вам не разрешено использовать constexpr:

class A
{
        char const* const const_per_instance = "Other Const String";
    public:
        static char const* const const_per_class  ;
};

char const* const A::const_per_class = "Hello World" ;

но суть та же, только синтаксис другой.

На ваш второй вопрос как Gotw #92 говорит, что auto отбрасывает константу верхнего уровня, один из приведенных примеров выглядит следующим образом:

const int   ci  = val;  
auto        g   = ci;

и он говорит:

Тип g — int.

Помните, только потому, что ci является константой (только для чтения), не имеет никакого отношения к тому, хотим ли мы, чтобы g была константой. Это отдельная переменная. Если бы мы хотели, чтобы g было константой, мы бы сказали const auto, как мы сделали в случае c выше.

пример, о котором идет речь, выглядит следующим образом:

int         val = 0;  
//..
const auto  c   = val;
person Shafik Yaghmour    schedule 30.09.2014
comment
К сожалению, constexpr не поддерживается в msvc 12 :-( - person void.pointer; 30.09.2014
comment
@void.pointer вам не нужен constexpr ответ не меняет только синтаксис. Я обновил ответ, включив в него пример без constexpr. - person Shafik Yaghmour; 30.09.2014

constexpr auto& MY_CONSTANT = "Hello World";
  • MY_CONSTANT имеет тип const char (&)[12]
  • Нет распада (привязка массива не теряется)
  • Постоянно всё - сам массив и ссылка (по определению)
  • Все constexpr (можно использовать во время компиляции) - сам массив и ссылка
  • MY_CONSTANT имеет внутреннюю связь из-за constexpr и может использоваться в заголовках
person user2665887    schedule 30.09.2014
comment
Вместо этого можно использовать VC++ static auto& MY_CONSTANT = "Hello World";. - person user2665887; 30.09.2014
comment
static не имеет ничего общего с постоянством. Это означает, что функция не видна в других модулях компиляции. - person NO_NAME; 29.05.2016
comment
Насколько я могу судить, constexpr не меняет связь, так что вам все равно понадобится static ? - person golvok; 05.09.2019

Это редкий случай, когда вы перезаписываете свой указатель, указывающий на константное значение, поэтому большинство разработчиков опускают вторую константу, но семантически это было бы действительно правильно:

static char const* const MY_CONSTANT = "Hello World";

или в таком виде:

static const char* const MY_CONSTANT = "Hello World";

constexpr для объявления просто необходим, если он является частью другой функции constexpr, например:

static constexpr const char* const MY_CONSTANT = "Hello World";
static constexpr const char* Foo()
{
    // ...
    return MY_CONSTANT;
}
person HelloWorld    schedule 30.09.2014

Молодец, что заметил константу в части указателя! Многие люди этого не понимают.

Чтобы предотвратить дублирование строкового литерала для каждой единицы перевода (или упростить часть оптимизатора, объединяющую строки), я предлагаю поместить фактические данные где-нибудь в исходный файл. Это также сэкономит время на перекомпиляцию, если вы измените текст.

заголовок:

extern const char *const mystring;

источник:

extern const char *const mystring = "hello";

Альтернативно

заголовок:

extern const std::string mystring;

источник:

extern std::string mystring = "hello";

person Neil Kirk    schedule 30.09.2014