Почему я не могу сделать инициализированный в классе `const const std::string` статическим членом

У меня есть следующий рабочий код:

#include <string>
#include <iostream>

class A {
public:
  const std::string test = "42";
  //static const std::string test = "42"; // fails
};

int main(void){
  A a;
  std::cout << a.test << '\n';
}

Есть ли веская причина, почему нельзя сделать тест static const? Я понимаю, до C++11 это было ограничено стандартом. Я думал, что C++11 ввел инициализацию в классе, чтобы сделать его немного более дружественным. Я также не такой семантический для целочисленного типа доступен с давних пор.

Конечно, это работает с внеклассовой инициализацией в форме const std::string A::test = "42";

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

Однако теперь ответственность за это перекладывается на пользователя / программиста. Если кто-то хочет иметь библиотеку с static они должны предоставить определение вне класса, скомпилировать его в отдельный объектный файл, а затем связать все другие объекты с этим, следовательно, имея только одну копию двоичного определения символа.

Я прочитал ответы в разделе. Нужно ли нам отдельно определять статические члены, даже если они инициализируются внутри определения класса? и почему я не могу инициализировать неконстантный статический член или статический массив в классе?,

Я все еще хотел бы знать:

  1. Это только стандартная вещь, или за этим стоит более глубокая аргументация?
  2. Может ли это быть обойти с constexpr и пользовательские литеральные механизмы. И clang, и g++ говорят, что переменная не может иметь не-литеральный тип. Может быть, я могу сделать один. (Может по какой-то причине это тоже плохая идея)
  3. Неужели для линкера действительно так сложно включить только одну копию символа? Так как это static const все должны быть точными двоичными неизменными копиями.

Просьба также прокомментировать, если я что-то упускаю или неправильно понимаю.

1 ответ

Решение

Ваш вопрос вроде состоит из двух частей. Что говорит стандарт? А почему так?

Для статического члена типа const std::string, он должен быть определен вне спецификатора класса и иметь одно определение в одной из единиц перевода. Это является частью правила One Definition Rule и определено в разделе 3 стандарта C++.

Но почему?

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

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

Однако сложный тип, как std::string, при статической длительности хранения нужно хранение, даже если они const, Это потому, что им может потребоваться динамическая инициализация (вызов их конструктора перед входом в main).

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

Другие вопросы по тегам