Здравствуйте, wander, Вы писали:
IRO>>Для меня UB это запрет, просто трудно уловимый компилятором(поэтому он и не ругаеться). W>Если на то пошло, то UB там из-за использования ссылки на невалидную память, а не из-за порядка инициализации. Порядок иницализации тут просто поспособствовал этому хорошо.
Ну я именно это и хотел сказать, что дело не в порядке, а вообще в том что мы используем(разыменовываем) неготовый указатель.
Здравствуйте, wander, Вы писали:
W>Местами поменяй инициализацию. Работаешь же с неинициализированной памятью.
Чтото мне подсказывает что такая работа запрещена стандартом, хотя могу ошибаться
Здравствуйте, Abyx, Вы писали:
A>Это понятно. Мне интересно какие меры надо принять чтобы не повторить такую ошибку в будущем.
Ну что тут сказать. GCC это тоже не диагностирует. Поэтому остается только работать над собой, вырабатывать дисциплину и всегда проверять что за чем должно инициализироваться. А еще лучше отказаться по возможности от написания подобного кода. Ибо error prone.
Здравствуйте, wander, Вы писали:
W>Здравствуйте, IROV.., Вы писали:
IRO>>Чтото мне подсказывает что такая работа запрещена стандартом, хотя могу ошибаться W>Да что вы говорите :)
3.8/5
Before the lifetime of an object has started but after the storage which the object will occupy has been allocated34)
or, after the lifetime of an object has ended and before the storage which the object occupied is
reused or released, any pointer that refers to the storage location where the object will be or was located
may be used but only in limited ways. Such a pointer refers to allocated storage (3.7.3.2), and using the
pointer as if the pointer were of type void*, is welldefined.
Such a pointer may be dereferenced but the
resulting lvalue may only be used in limited ways, as described below. If the object will be or was of a
class type with a nontrivial
destructor, and the pointer is used as the operand of a deleteexpression,
the
program has undefined behavior. If the object will be or was of a nonPOD
class type, the program has
undefined behavior if:
— the pointer is used to access a nonstatic data member or call a nonstatic member function of the object, or
— the pointer is implicitly converted (4.10) to a pointer to a base class type, or
— the pointer is used as the operand of a static_cast (5.2.9) (except when the conversion is to
void*, or to void* and subsequently to char*, or unsigned char*).
— the pointer is used as the operand of a dynamic_cast (5.2.7).
Здравствуйте, qqqqq, Вы писали:
Q>gcc выдаст warning в этом случае.
У меня 4.5 не выдает.
-Wextra -Wall -ansi -pedantic -Weffc++
Comeau тоже не выдает.
Кроме того, мне кажется правильно, что не выдает. С какой стати-то
Q>Кроме того, статические анализаторы кода должны такие и другие подобные ошибки ловить.
Это да.
Позвольте, UB — это не запрещение. Это всего лишь предупреждение о том, что за последствия никто не ручается. Вот если бы такое не компилировалось, тогда да — запрещение.
Здравствуйте, qqqqq, Вы писали:
Q>gcc выдаст warning в этом случае. Кроме того, статические анализаторы кода должны такие и другие подобные ошибки ловить.
Здравствуйте, Abyx, Вы писали:
A>Как такого избежать?
Я всегда сразу же после добавления нового члена в класс, добавляю его в список инициализации в конструкторе, новые члены добавляю или в конец, или в конец логической группы членов, чтобы точно случайно не промахнуться и в списке инициализации все члены шли в порядке объявления, оно конечно примитивно и не ахти как идейно, но наряду с обязательной инициализацией локальных переменных, ассертами, подробными трэйсами и прочими штуками, без которых можно как бы и обойтись, в итоге сохраняет кучу времени =)
Здравствуйте, wander, Вы писали:
W>Позвольте, UB — это не запрещение. Это всего лишь предупреждение о том, что за последствия никто не ручается. Вот если бы такое не компилировалось, тогда да — запрещение.
буквоед?
Для меня UB это запрет, просто трудно уловимый компилятором(поэтому он и не ругаеться).
Здравствуйте, IROV.., Вы писали:
IRO>буквоед?
Не виноват я, оно само так
IRO>Для меня UB это запрет, просто трудно уловимый компилятором(поэтому он и не ругаеться).
Если на то пошло, то UB там из-за использования ссылки на невалидную память, а не из-за порядка инициализации. Порядок иницализации тут просто поспособствовал этому хорошо.
Здравствуйте, Abyx, Вы писали:
A>Здравствуйте, IROV.., Вы писали:
IRO>>Писать
IRO>>: bar(this->foo)
IRO>>Тогда получишь варнинг
A>очевидно это не тот варнинг
Тот-тот, он как раз и предупреждает программиста о том что — по мнению компилятора, мы здесь используем непроинициализированый указатель(this), и добром это не кончеться.
A>еще можно написать "bar(!@#$%^)" и получить error
как щас модно говорить в геймерских кругах, "удали студию"
2.
У меня в кодестайле прописано, что в конструкторах нельзя выполнять функции.
Если нужно пишем — initialize, если до, значит нужен какойто менеджер.
Здравствуйте, IROV.., Вы писали:
IRO>Ну я именно это и хотел сказать, что дело не в порядке, а вообще в том что мы используем(разыменовываем) неготовый указатель.
никакого разыменования (использования) там нет, вариант с this-> ты придумал сам.
Здравствуйте, Хвост, Вы писали:
Х>Здравствуйте, IROV.., Вы писали:
IRO>>Ну я именно это и хотел сказать, что дело не в порядке, а вообще в том что мы используем(разыменовываем) неготовый указатель. Х>никакого разыменования (использования) там нет, вариант с this-> ты придумал сам.
Здравствуйте, Хвост, Вы писали:
Х>Здравствуйте, IROV.., Вы писали:
IRO>>Ну я именно это и хотел сказать, что дело не в порядке, а вообще в том что мы используем(разыменовываем) неготовый указатель. Х>никакого разыменования (использования) там нет, вариант с this-> ты придумал сам.
Как это нет??!
struct Bar
{
Bar(Foo & f)
{
//...f.load();//а это что?
}
};
Здравствуйте, Abyx, Вы писали:
A>Сейчас где-то час потратил на то чтобы найти баг в следующем коде:
A>
A>#include <vector>
A>struct Foo
A>{
A> Foo() {}
A> void load(/*...*/)
A> {
A> v.clear(); // тут возникает assert "итераторы несовместимы"
A> }
A> std::vector<int> v;
A>};
A>struct Bar
A>{
A> Bar(Foo& f)
A> {
A> //...
A> f.load(/*...*/);
A> }
A>};
A>struct Holder
A>{
A> Holder()
A> : bar(foo)
A> , foo()
A> {}
A> Bar bar;
A> Foo foo;
A>};
A>int main()
A>{
A> Holder h;
A>}
A>
я вообще не об этом говорю, я говорю о том что использование проинициализированных полей объекта до полной инициализации самого объекта не является UB, как в этом пытается убедить нас IROV.
Здравствуйте, Хвост, Вы писали:
Х>Здравствуйте, wander, Вы писали:
W>>Как это нет??!
Х>я вообще не об этом говорю, я говорю о том что использование проинициализированных полей объекта до полной инициализации самого объекта не является UB, как в этом пытается убедить нас IROV.
Так тут как раз используется непроинициализированное. Что касается выделенного, мне не кажется, что он в этом пытается нас убедить.
Здравствуйте, Abyx, Вы писали:
A>Здравствуйте, martin, Вы писали:
M>>Посмотри в сторону boost::base_from_member.
A>посмотрел и не понял что вы хотите предложить.
Насколько я понимаю, в вашем примере проблема в том что bar пытается использовать объект foo который еще не создан. Проблему конечно можно избежать поменяв местами объявления членов bar и foo класса Holder. Тогда все будет ок. Но в будущем нельзя гарантировать что кто-нибудь их опять не поменяет случайно местами. Самое правильное, на мой взгляд, это сделать гарантированный вызов конструктора foo перед вызовом конструктора bar. Этого можно добиться с использованием наследования.