Подтвердить что ты не робот

В инициализации члена класса происходит во время компиляции или времени выполнения?

В С++ 11 появилась новая функция, в которой программист может инициализировать переменные-члены класса внутри определения класса, см. следующий код:

struct foo
{ 
  int size = 3;
  int id   = 1;
  int type = 2;
  unsigned char data[3] = {'1', '2', '3'};
};

Эта инициализация выполняется во время компиляции, или эта функция является просто синтаксическим сахаром, а переменные-члены инициализируются в конструкторе по умолчанию?

4b9b3361

Ответ 1

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

У вас есть класс С++ 11, который имеет в инициализаторах класса

struct foo { int size = 3; };

И еще один класс, который поможет нам в нашем эксперименте

template<int N>
struct experiment { enum { val = N }; };

Пусть наша гипотеза H0 заключается в том, что инициализация происходит во время компиляции, тогда мы могли бы написать

foo                a;
experiment<a.size> b;

Не повезло, мы не скомпилируем. Можно утверждать, что отказ вызван тем, что foo::size не является константой, поэтому попробуйте с

struct foo { const int size = 3; }; // constexpr instead of const would fail as well

Опять же, поскольку gcc сообщает нам

значение 'a не может использоваться в постоянном выражении

эксперимент b;

или (более четко) visual studio 2013 говорит нам

ошибка C2975: 'N': неверный аргумент шаблона для 'example', ожидаемое выражение константы времени компиляции

Итак, мы должны отбросить H0 и вывести, что инициализация не выполняется во время компиляции.

Что произойдет во время компиляции

Существует старый синтаксис, который делает трюк

struct foo { static const int size = 3; };

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

Мне нужно было немножко солгать, но чтобы разоблачить всю правду сейчас: Ошибки сообщения подразумевают, что a является реальной проблемой. Вы видите, поскольку у вас есть экземпляр объекта (Daniel Frey также упоминает об этом) память (для членов) должна быть инициализирована (во время выполнения). Если член был (const) static, как в последнем примере, то он не является частью подобъектов класса (ny), и вы можете инициализировать его во время компиляции.

Ответ 2

Инициализаторы класса для переменных-членов являются синтаксическим сахаром для их записи в списке инициализации конструктора, если там не существует явного инициализатора, и в этом случае они игнорируются.
In-class инициализаторы статических константных членов для постоянных литералов, определение все еще необходимо (хотя без инициализатора).

С++ имеет "как будто" -уровень от C, поэтому допускается любое, что приводит к предписанному наблюдаемому поведению.
В частности, это означает, что статические объекты могут быть инициализированы во время компиляции.

Ответ 3

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

Ответ 4

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

Надеюсь, эти примеры помогут. Попробуйте их в http://gcc.godbolt.org и посмотрите ошибки дизассемблирования и компиляции.

struct S { int size = 3; };

//s data members are compile time constants
constexpr S s = {};

//r data members are run time constants
const S r = {};

//rr data members are run time constants, 
//but we don't know the values in this translation unit
extern const S rr;

template <int X> class Foo {};

//Ok, s.size is a compile time expression
Foo<s.size> f; 

//Error, r.size is not a compile time expression
Foo<r.size> g; 

//Compile time expression, this is same as return 3;
int foo() { return s.size; }

//This also works
constexpr int cfoo() { return s.size; }

//Compiler will optimize this to return 3; because r.size is const.
int bar() { return r.size; }

//Compiler cannot optimize, because we don't know the value of rr.size
//This will have to read the value of rr.size from memory.
int baz() { return rr.size; }

Как показали другие, статические члены данных (и глобальные переменные, то же самое по существу) для примитивных типов, таких как ints и floats, имеют некоторые странные правила, где они могут быть const, но все же использоваться в контекстах времени компиляции, как если бы они были constexpr, Это для обратной совместимости с C и отсутствия функции constexpr в прошлом. Его неудачный сейчас, потому что он просто делает понимание constexpr и что отличает выражения времени выполнения от компиляции выражений времени более запутанным.

Ответ 5

Наличие int size = 3; в точности эквивалентно наличию int size;, а затем каждый конструктор, который еще не имеет size в своем списке инициализатора (включая конструкторы, сгенерированные компилятором), имеющего size(3).

Строго говоря, на С++ нет различия между "временем компиляции" и "временем выполнения".