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

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

static char const* MY_CONSTANT = "Hello World";

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

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

Есть два гола с постоянными глобальными подобными, я думаю:

  • Не разрешать модификацию строки
  • Не позволяйте переменной указывать на что-либо еще

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

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

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

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

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

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

Ответ 1

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

constexpr char const* MY_CONSTANT = "Hello World";

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

static char const* MY_CONSTANT = "Hello World";

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

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

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

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

Если мы требуем, чтобы const был для каждого класса, тогда нам нужно использовать 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, один приведенный ниже пример выглядит следующим образом:

const int   ci  = val;  
auto        g   = ci;

и он говорит:

Тип g - int.

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

приведенный ниже пример выглядит следующим образом:

int         val = 0;  
//..
const auto  c   = val;

Ответ 2

constexpr auto& MY_CONSTANT = "Hello World";
  • MY_CONSTANT имеет тип const char (&)[12]
  • Отсутствие распада (граница массива не потеряна)
  • Все постоянное - сам массив и ссылка (по определению)
  • Все это constexpr (может использоваться во время компиляции) - сам массив и ссылка
  • MY_CONSTANT имеет внутреннюю связь из-за constexpr и может использоваться в заголовках

Ответ 3

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

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

Ответ 4

Хорошо сделано для того, чтобы заметить const на части указателя! Многие люди этого не понимают.

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

Заголовок:

extern const char *const mystring;

Источник:

extern const char *const mystring = "hello";

В качестве альтернативы

Заголовок:

extern const std::string mystring;

Источник:

extern std::string mystring = "hello";