Здравствуйте, Videoman, Вы писали:
V>Класс станет не POD, но std::string -то нормально инициализируется. Т.е. всё зависит от самим членов структуры, так что, я считаю, это нормальное поведение.
Я про другое — non-POD класс по-разному инициализируется при default и при value инициализации. Такое возможно только если конструктор сгенерирован компилятором. Если дефолтный конструктор определен программистом, то оба вида инициализации будут работать строго одинаково — через конструктор, предоставленный программистом. То есть, не может программист определить дефолтный конструктор, равнозначный тому, который может сгенерировать компилятор.
Здравствуйте, imh0, Вы писали:
I>По теме вопроса — посмотрите, это действительно важно — будет ли у вас работать такое...
I>map<int,{ваш тип}> mmm;
I>mmm[234]++;
I>При условии, что по умолчанию, предположим, в вашем типе, поумолчанию ноль.
Будет работать, почему нет ?! Если такого ключа нет, то он проинициализируется конструктором по умолчанию, также как простой int.
V>??? V>Что POD мемберы инициализируются как все POD, а не-POD как нормальные классы? По-моему это логично, так и должно быть.
Меня смущает, что я, как разработчик, не могу предоставить собственную реализацию дефолтного конструктора, обладающего таким же поведением, как у конструктора, сгенерированного компилятором.
Здравствуйте, rg45, Вы писали:
R>Меня смущает, что я, как разработчик, не могу предоставить собственную реализацию дефолтного конструктора, обладающего таким же поведением, как у конструктора, сгенерированного компилятором.
Опиши какую проблему ты рассматриваешь и когда это может понадобиться?
Здравствуйте, Videoman, Вы писали:
R>>Меня смущает, что я, как разработчик, не могу предоставить собственную реализацию дефолтного конструктора, обладающего таким же поведением, как у конструктора, сгенерированного компилятором.
V>Опиши какую проблему ты рассматриваешь и когда это может понадобиться?
Вот прямо реальную задачу придумать сходу затрудняюсь. Но допустим, по каким-то причинам конструктор, сгенерированный компилятором (объявленный как "= default"), не подходит и хочется дать собственное полное определение (со списком инициализации и с телом). В этом случае мы тут же возвращаемся к той проблеме, которая заставила тебя создать эту тему. Конечно, тут самое время вспомнить о существовании default member initializers, которые позволяют разрулить самые сложные потребности при инициализации. Тем не менее, лично у меня как-то вот нет уверенности, что таким образом можно разрулить 100% всех проблем и обойтись без конструктора, определенного пользователем.
Здравствуйте, Videoman, Вы писали:
R>>Меня смущает, что я, как разработчик, не могу предоставить собственную реализацию дефолтного конструктора, обладающего таким же поведением, как у конструктора, сгенерированного компилятором.
V>Опиши какую проблему ты рассматриваешь и когда это может понадобиться?
Я вот тут подумал хорошенько и пришел к выводу, что все, что я писал постом выше — от лукавого. Формально: сгенерированный компилятором дефолтный конструктор порождает различное поведение для default и для value инициализаций. Я хотел похожего поведения и для дефолтных конструкторов, определенных пользователем. То есть, по сути, возможности написать разные конструкторы для этих двух видов инициализации. При этом придумать какую-нибудь прикладную задачу, при решении которой эта фича могла бы оказаться полезной я так и не смог. Зато представил, сколько новых вопросов может поднять появление нового вида конструктора. Пришел к выводу, что незачем усложнять язык без видимых на то причин.
Здравствуйте, rg45, Вы писали:
R>Я вот тут подумал хорошенько и пришел к выводу, что все, что я писал постом выше — от лукавого. Формально: сгенерированный компилятором дефолтный конструктор порождает различное поведение для default и для value инициализаций. Я хотел похожего поведения и для дефолтных конструкторов, определенных пользователем. То есть, по сути, возможности написать разные конструкторы для этих двух видов инициализации. При этом придумать какую-нибудь прикладную задачу, при решении которой эта фича могла бы оказаться полезной я так и не смог. Зато представил, сколько новых вопросов может поднять появление нового вида конструктора. Пришел к выводу, что незачем усложнять язык без видимых на то причин.
Я как раз с этого начал и, в итоге, ты мне напомнил нормальное решение, так что для меня было сюрпризом что обсуждение пошло на второй круг .
R>А тебе принципиально, чтобы этот конструктор был определен тобой? Автоматически сгенерированный компилятором дефолтный конструктор не подойдет тебе? В этом случае ты можешь иметь в классе набор каких-то кастомных конструторов, но при этом класс сохранит поведение при инициализации подобное POD-ам:
Тут, кстати, узнал, что С++ еще по разному трактует место в котором конструктор объявлен как default
#include <iostream>
struct foo {
foo() = default;
int a;
};
struct bar {
bar();
int b;
};
bar::bar() = default;
int main() {
foo a{};
bar b{};
std::cout << a.a << ' ' << b.b;
}
Здравствуйте, Максим, Вы писали:
М>Конструктор bar в этом примере, с точки зрения компилятора, уже user-defined и соответсвенно приходит ub и произвольное значение для b.b.
Класс! А что тогда теперь делать если bar содержит какой-нибудь incomplete type в качестве члена, типа:
V>Класс! А что тогда теперь делать если bar содержит какой-нибудь incomplete type в качестве члена. V>и default конструктор нужно реализовать позже, где-нибудь в cpp файле ?
Да, могут возникнуть проблемы. Я на что-то подобное наткнулся как раз при играх с pimpl паттерном. С++ не дает расслабиться
П.С.
Забыл сказать. Clang на маке с опциями -Wall — Wextra не выдавал предупреждения на то, что переменная не инициализирована, потом проверил с gcc, тут предупреждение было. Такие вещи добавляют еще красок в отладку.
Здравствуйте, Videoman, Вы писали:
V>Здравствуйте, flаt, Вы писали:
F>>В лоб не получится, но можно подумать о тегах и вызывать разные конструкторы.
V>Жаль. В принципе я готов к такой реальности. Вопрос на самом деле возник лишь из-за удобства и надежности написания шаблонного кода. Просто не зная какой конкретно тип передается мне придется писать:
V>type_t a; // Если я не хочу "занулять" экземпляр и,
V>type_t a(0); // Если я хочу "занулить" экземпляр. Что накладывает определенные ограничения на типы, чего хотелось бы избежать.
V>Жаль. В принципе я готов к такой реальности. Вопрос на самом деле возник лишь из-за удобства и надежности написания шаблонного кода. Просто не зная какой конкретно тип передается мне придется писать:[ccode] V>Весь сыр-бор разгорелся из-за кода в шаблонной библиотеке, где я по привычке вызываю type_t() для инициализации. Потом оказалось что в моем собственном классе T, который по сути тоже POD, конструктор по умолчанию, с инициализацией нулями, жрал слишком много CPU и инициализацию пришлось убрать. После этого шаблонный код type_t() стал работать то инициализируя, то нет, в зависимости от переданного типа. Пока всё это лишь вопрос удобства и надежности кода.
А нельзя определить POD или не POD, и делать по-разному?
Здравствуйте, Marty, Вы писали:
M>А нельзя определить POD или не POD, и делать по-разному?
Можно конечно "магию" применить, но во-первых — зачем такие сложности на ровном месте, а во-вторых — я не уверен что в будущем это не выльется в ещё какие-нибудь проблемы. Всегда думал что чем проще, тем лучше.
V>и default конструктор нужно реализовать позже, где-нибудь в cpp файле ?
Насколько я понял, конструктор для bar.m_i всё равно вызывается, так что тут ничего страшного, а поля типа int можно инициализировать прямо при объявлении:
#include <iostream>
struct foo {
foo() = default;
int a;
};
struct bar {
bar();
int b = 0;
};
bar::bar() = default;
int main() {
foo a{};
bar b{};
std::cout << a.a << ' ' << b.b;
}
Здравствуйте, Videoman, Вы писали:
М>>Конструктор bar в этом примере, с точки зрения компилятора, уже user-defined и соответсвенно приходит ub и произвольное значение для b.b.
V>Класс!
Что-то не соображу, какую практическую пользу можно извлечь из этой возможности.
М>struct bar {
М> bar();
М> int b;
М>};
М>bar::bar() = default;
М>
Интересно, такое определение конструктора чем-то отличается от традиционного:
bar::bar(){}
?
По-моему, они дают один и тот же эффект: все подобъекты non-POD типов будут проинициализированы их дефолтными конструкторами, а все POD-ы останутся без инициализации — в обоих вариантах. И zero-initialization в обоих вариантах оказывается недоступной.
Здравствуйте, rg45, Вы писали:
R>Что-то не соображу, какую практическую пользу можно извлечь из этой возможности.
Для конструктора, наверное, пользы никакой, но для деструктора может быть удобно подчеркивает намерения автора кода.
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Здравствуйте, rg45, Вы писали:
R>По-моему, они дают один и тот же эффект: все подобъекты non-POD типов будут проинициализированы их дефолтными конструкторами, а все POD-ы останутся без инициализации — в обоих вариантах. И zero-initialization в обоих вариантах оказывается недоступной.
Да нет же. Если написать:
// в .h или .cpp - не важно где
bar::bar(){};
// или в .cpp
bar::bar() = default;
то:
bar b; // POD члены не будут инициализированы, т.е. как и у любого стандартного POD типаauto b = bar(); // POD члены также не будут инициализированы, что неожиданно в случае default
Если же написать:
// именно в .h
bar::bar() = default;
то:
bar b; // POD члены не будут инициализированыauto b = bar(); // POD члены станут нулями, как и ожидаешь от любого конструктора по умолчанию