Действительно ли мне нужно реализовать созданный пользователем конструктор для объектов const?
У меня есть код:
class A {
public:
A() = default;
private:
int i = 1;
};
int main() {
const A a;
return 0;
}
Он компилируется отлично на g++ (см. ideone), но с ошибкой: clang++ с ошибкой:
инициализация по умолчанию объекта const типа 'const A' требует созданного пользователем конструктора по умолчанию
Я сообщил об этой проблеме в LLVM bug-tracker и получил ее INVALID.
Я вижу совершенно бессмысленно пытаться убедить разработчиков clang. С другой стороны, я не вижу причины такого ограничения.
Может ли кто-нибудь посоветовать, если С++ 11 Standard так или иначе подразумевает, что этот код недействителен? Или я должен просто сообщить об ошибке g++? Или, может быть, существует достаточная свобода в языковых правилах для обработки этого кода во многих отношениях?
Ответы
Ответ 1
N3797 §8.5/7 гласит:
Если программа запрашивает инициализацию по умолчанию объекта типа const, T, T должен быть типом класса с предоставленным пользователем конструктором по умолчанию.
Нет никакого другого примера или объяснения этого. Я согласен, что это выглядит довольно странно. Кроме того, правило было обновлено на С++ 11, чтобы быть более ограничительным, чем в С++ 03, когда типы классов нуждались в конструкторах, объявленных пользователем. (Ваш конструктор объявляется пользователем.)
Обходным путем является запрос инициализации значения с помощью {}
или использование определения Dietmar умного вне класса inline
.
GCC предоставляет диагноз (и довольно хороший, ссылаясь на более новые требования к С++ 11), если вы добавляете другой элемент без инициализатора.
private:
int i = 1;
int j;
unmem.cpp:11:11: error: uninitialized const ‘a’ [-fpermissive]
const A a;
^
unmem.cpp:1:7: note: ‘const class A’ has no user-provided default constructor
class A {
^
unmem.cpp:3:5: note: constructor is not user-provided because it is explicitly defaulted in the class body
A() = default;
^
unmem.cpp:7:9: note: and the implicitly-defined constructor does not initialize ‘int A::j’
int j;
Источник GCC относится к DR 253, почему должны быть инициализированы пустые или полностью инициализированные объекты const? Это открытая проблема в стандарте, последняя обновленная в августе 2011 года (post-С++ 11) с этой заметкой:
Если неявный конструктор по умолчанию инициализирует все подобъекты, не требуется инициализатор.
Поэтому, если Clang соответствует С++ 11 (и будет соответствовать требованиям С++ 14), GCC внедряет последнее мышление комитета по стандартизации.
Подана ошибка GCC. Я предсказываю, что вам понадобится -pedantic
, чтобы получить диагноз, когда (и если) ошибка исправлена.
Ответ 2
Обратите внимание, что вы можете легко превратить свой класс в тот, у которого есть пользовательский конструктор по умолчанию:
class A {
public:
A();
private:
int i = 1;
};
inline A::A() = default;
В соответствии с пунктом 8.4.2 [dcl.fct.def.default], пункт 4:
... Специальная функция-член предоставляется пользователю, если она объявлена пользователем и не явно по умолчанию или удалено по его первой декларации....
Это подразумевает, что функция, которая явно не дефолтна по первому объявлению, не предоставляется пользователям. В сочетании с 8,5 [dcl.init] абзацем 6
... Если программа вызывает инициализацию по умолчанию объекта типа const, квалифицированного по типу T, T должен быть типом класса с предоставленным пользователем конструктором по умолчанию.
кажется ясным, что вы не можете использовать конструктор по умолчанию, по умолчанию для своего первого объявления, для инициализации объекта const
. Однако вы можете использовать определение по умолчанию, если оно не является первым объявлением, как это сделано в приведенном выше коде.
Ответ 3
Изменить: Ниже приведена устаревшая информация. Я только что прошел через N3797, и вот что я нашел:
§ 8.5/7 [dcl.init]
Если программа вызывает инициализацию по умолчанию объекта const-квалифицированного типа T, T должен быть типом класса с a пользовательский конструктор по умолчанию.
Обратите внимание на стандартную цитату в приведенной ниже ссылке: объявленный пользователем.
Следующая программа компилируется в g++, но не clang++:
struct A {};
void f()
{
A const a;
}
И это может быть связано с этим отчет об ошибке, где он был "исправлен". g++ не может скомпилировать его, если он содержит данные, если они не инициализированы. Обратите внимание, что int member = 1
больше не сделает A
POD. Сравнительно, clang++ отвергает все перестановки (пустые классы и члены данных инициализированы или нет). Для интерпретации того, что стандарт означает в следующем абзаце:
§ 8.5/9 [dcl.init] говорит:
Если для объекта не задан инициализатор, а объект имеет значение (возможно, cv-qualified) тип класса не-POD (или его массив), объект должен быть инициализирован по умолчанию; если объект имеет const-qual type, тип базового класса должен иметь объявленный пользователем конструктор по умолчанию. В противном случае, если инициализатор не установлен заданный для объекта, объект и его подобъекты, если они есть, имеют неопределенное начальное значение; если объект или любой из его подобъектов имеют const-квалифицированный тип, программа плохо сформирована.
См. Почему С++ требует, чтобы пользовательский конструктор по умолчанию устанавливал по умолчанию объект const?. Предположительно программа плохо сформирована if the object is of const-qualified POD type, and there is no initializer specified (because POD are not default initialized).
Обратите внимание, как g++ ведет себя следующим образом:
struct A {int a;};
struct B {int a = 1;};
int main()
{
A a;
B b;
const A c; // A is POD, error
const B d; // B is not POD, contains data member initializer, no error
}