Почему члены const могут быть изменены в конструкторе?
Мне любопытно, почему члены const могут быть изменены в конструкторе.
Существует ли стандартное правило инициализации, которое переопределяет "константу" члена?
struct Bar {
const int b = 5; // default member initialization
Bar(int c):b(c) {}
};
Bar *b = new Bar(2); // Problem: Bar::b is modified to 2
// was expecting it to be an error
Есть идеи?
Ответы
Ответ 1
Это не модификация (или назначение), а инициализация. например
struct Bar {
const int b = 5; // initialization (via default member initializer)
Bar(int c)
:b(c) // initialization (via member initializer list)
{
b = c; // assignment; which is not allowed
}
};
Член данных const
не может быть изменен или назначен, но он может (и должен быть) инициализирован через список инициализаторов членов или инициализатор элемента по умолчанию.
Если оба инициализатора элемента инициализации и инициализатор элемента представлены в одном элементе данных, инициализатор элемента по умолчанию будет проигнорирован. Поэтому b->b
инициализируется значением 2
.
Если член имеет инициализатор элемента по умолчанию и также появляется в списке инициализации члена в конструкторе, инициализатор элемента по умолчанию игнорируется.
С другой стороны, инициализатор элемента по умолчанию вступает в силу только тогда, когда член данных не указан в списке инициализаторов членов. например
struct Bar {
const int b = 5; // default member initialization
Bar(int c):b(c) {} // b is initialized with c
Bar() {} // b is initialized with 5
};
Ответ 2
Добавляя к songyuanyao отличный ответ, если вы хотите, чтобы член данных const
который вы не можете инициализировать в конструкторе, вы можете сделать элемент static
:
struct Bar {
static const int b = 5; // static member initialization
Bar(int c)
:b(c) // Error: static data member can only be initialized at its definition
{
b = c; // Error: b is read-only
}
};
В С++ 17 вы можете улучшить это, сделав его inline
:
struct Bar {
inline static const int b = 5; // static member initialization
Bar(int c)
:b(c) // Error: static data member can only be initialized at its definition
{
b = c; // Error: b is read-only
}
};
Таким образом, у вас не будет проблем с ODR.
Ответ 3
Когда вы выполните:
struct Bar {
const int b = 5; // default member initialization
...
};
Вы сообщаете компилятору сделать это с помощью конструктора по умолчанию:
...
Bar() : b(5)
{}
...
Независимо от того, предоставлен ли по умолчанию конструктор или нет. Когда вы предоставляете конструктор по умолчанию и начальное назначение, вы переопределяете код назначения по умолчанию для компилятора (т.е. b(5)
). Инициализация/назначение по умолчанию в объявлении полезно, когда у вас есть несколько конструкторов, и вы можете или не можете назначать члены const во всех конструкторах:
...
Bar() = default; // b=5
Bar(int x) : b(x) // b=x
Bar(double y) : /*other init, but not b*/ // b=5
...