Существует ли стандартная возможность определения переменной-объекта без ее одновременно сопутствующего объявления. Например, для MSVC легален следующий код:
SomeClass sc(2);
...
someFunc(sc);
if(!sc.isValid())
sc.SomeClass::SomeClass(7); // объявления нет, только определение
otherFunc(sc);
...
В частности, если в SomeClass запрещены копирующий конструктор и "=", то есть проблемы.
pasenger пишет:
> Существует ли *стандартная* возможность определения переменной-объекта > без ее одновременно сопутствующего объявления. Например, для MSVC > легален следующий код: > > SomeClass sc(2); > ... > someFunc(sc); > if(!sc.isValid()) > sc.SomeClass::SomeClass(7); // объявления нет, только определение > otherFunc(sc); > ...
if(!sc.isValid())
SomeClass(7); // чем не подходит ? Стандартно, переносимо. Не объявляет.
ИЛИ
if(!sc.isValid())
new (&sc) SomeClass(7); // тоже стандартно.
Здравствуйте, MasterZiv, Вы писали:
MZ> new (&sc) SomeClass(7); // тоже стандартно.
Не знаю что там стандартно, кроме непредсказуемого поведения, разумеется
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, MasterZiv, Вы писали:
MZ>pasenger пишет:
>> Существует ли *стандартная* возможность определения переменной-объекта >> без ее одновременно сопутствующего объявления. Например, для MSVC >> легален следующий код: >> >> SomeClass sc(2); >> ... >> someFunc(sc); >> if(!sc.isValid()) >> sc.SomeClass::SomeClass(7); // объявления нет, только определение >> otherFunc(sc); >> ...
MZ> if(!sc.isValid()) MZ> SomeClass(7); // чем не подходит ? Стандартно, переносимо. Не объявляет.
template<typename T>
void setBySwap( T &dst, T toSet ) // такой тип toSet выбран специально!
{
std::swap( dst, toSet )
}
SomeClass sc(2);
..
someFunc(sc);
if(!sc.isValid())
setBySwap<SomeClass>( sc, 7 );
otherFunc(sc);
...
P>В частности, если в SomeClass запрещены копирующий конструктор и "=", то есть проблемы.
Обычно в любом случае можно определить (если ещё не определён std::swap)
Но ещё прямее, конечно, иметь у sc метод для переинициализации.
В любом случае твой код неверне потому, что не зовёт деструктор более позднего объекта, и не вовремя зовёт деструктор более раннего...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
E>template<typename T>
E>void setBySwap( T &dst, T toSet ) // такой тип toSet выбран специально!
E>{
E> std::swap( dst, toSet )
E>}
E>SomeClass sc(2);
E>..
E>someFunc(sc);
E>if(!sc.isValid())
E> setBySwap<SomeClass>( sc, 7 );
E>otherFunc(sc);
E>...
P>>В частности, если в SomeClass запрещены копирующий конструктор и "=", то есть проблемы. E>Обычно в любом случае можно определить (если ещё не определён std::swap)
Не совсем понимаю, что имеется ввиду. В моей версии STL std::swap определен следующим образом
и как такой swap способен помочь нашему SomeClass (как было сказано, без "=")?
E>Но ещё прямее, конечно, иметь у sc метод для переинициализации.
Не всегда есть возможность вмешиваться в интерфейс используемого класса.
E>В любом случае твой код неверне потому, что не зовёт деструктор более позднего объекта, и не вовремя зовёт деструктор более раннего...
Насколько я понимаю, деструктор раннего не зовется, а только позднего.
Здравствуйте, pasenger, Вы писали:
P>Существует ли стандартная возможность определения переменной-объекта без ее одновременно сопутствующего объявления.
Что за бред?
Давай разнесём такие вещи, как
— объявление и определение (относящиеся ко времени компиляции)
— инициализацию и присваивание (относящиеся ко времени исполнения)
P> Например, для MSVC легален следующий код:
P>SomeClass sc(2);
P>...
P>someFunc(sc);
P>if(!sc.isValid())
P> sc.SomeClass::SomeClass(7); // объявления нет, только определение
P>otherFunc(sc);
P>...
Повторное конструирование — это UB. Сделать-то ты можешь, но в ряде случаев результат будет разрушительным для работы программы.
P>В частности, если в SomeClass запрещены копирующий конструктор и "=", то есть проблемы.
Так бы сразу и сказал, что тебе хочется.
1) Не используй инициализацию в стиле присваивания.
SomeClass sc = 123;
// требуется, чтобы был доступен конструктор копирования,
// т.к. по сути это эквивалентно записи
SomeClass sc( SomeClass(123) );
// хотя компилятор и вправе сэкономить на копировании
SomeClass sc(123); // вуаля!
2) Откажись от повторно-конструируемых переменных в пользу
— присваивания
— — оператором = (с учётом всех неявностей)
— — специальной функцией "swap" (обмен содержимым) — перегрузка std::swap или собственные аналоги
— — специальной функцией "assign" (явное присваивание), например, у boost::scoped_ptr, где неявный operator= запрещён, есть метод reset; или у std::stream метод rdbuf (назначение streambuf)
— контейнеров — начиная просто от умных указателей до std::vector, практикующего placement new, или своих рукоделий
Ну а писать
if(!sc.isValid())
new(&sc)SomeClass(7); // стандартная форма, в отличие от ms-specific
это однозначно UB. Либо объект в первой строчке не сконструирован — тогда нельзя вызывать его методы; либо он сконструирован, тогда нельзя повторно конструировать.
Здравствуйте, pasenger, Вы писали:
E>>Обычно в любом случае можно определить (если ещё не определён std::swap)
P>Не совсем понимаю, что имеется ввиду. В моей версии STL std::swap определен следующим образом P>и как такой swap способен помочь нашему SomeClass (как было сказано, без "=")?
Для такого класса надо определить swap как-то иначе. обычно его можно определить, даже если нельзя определить конструктор копирования и оператор присваивания.
Мало того, в STL-based коде он часто определён и требование его определять, в случае, когда стандартная реализация не годится вполне так себе естественное.
E>>Но ещё прямее, конечно, иметь у sc метод для переинициализации. P>Не всегда есть возможность вмешиваться в интерфейс используемого класса.
Я так и не понял зачем нужно менять объект, если его семантика предполагает его неизменность?
E>>В любом случае твой код неверне потому, что не зовёт деструктор более позднего объекта, и не вовремя зовёт деструктор более раннего...
смотри, ты создаёшь по одному и тому же адресу два объекта. Один автоматический, а другой по new.
При этом зовётся деструктор автоматического, но на тот момент там уже лежат данные объекта созданного по new...
P>Очень часто можно позволить себе обойтись без вызова деструктора. В противном случае
1) Не понятно зачем иметь в классе деструктор, без которого можно обойтись?
Мало того, если класс POD или что-то около того, то можно и std::swap написать просто тупо меняя местами бинарные данные, и оператор присваивания всегда иметь...
P>
P>...
P> A a(4);
P> a.~A(); // Так нельзя. Никто не обещал, что так получится
P> a.A::A(5); // мало того, никто не обещал, что теперь второй деструктор отработает удачно...
P>...
P>
подправлена разметка — Кодт
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, pasenger, Вы писали:
P>>Существует ли стандартная возможность определения переменной-объекта без ее одновременно сопутствующего объявления.
К>Что за бред? К>Давай разнесём такие вещи, как К>- объявление и определение (относящиеся ко времени компиляции) К>- инициализацию и присваивание (относящиеся ко времени исполнения)
Возможно у меня проблемы с терминологией. Попробую объяснить, что я имею ввиду. Есть задача введения переменной в область видимости: публикация её имени и типа (A declaration makes known the type and name of the variable to the program). И есть задача "овеществления" переменной: появление физического объекта соответствующего продекларированному имени. (A definition of a variable allocates storage for the variable and may also specify an initial value for the variable). Вообще говоря, данные задачи различны. И хотелось, чтобы синтаксис позволял выполнять их раздельно. В плюсах же с этим проблемы, как оказалось. A definition is also a declaration: When we define a variable, we declare its name and type. We can declare a name without defining it by using the extern keyword. A declaration that is not also a definition consists of the object's name and its type preceded by the keyword extern:
extern int i; // declares but does not define iint i; // declares and defines i
Т.о. в плюсах обе задачи сильно связаны. Видимо, поэтому в плюсах виртуальный конструктор — это паттерн, а в дельфях — синтаксическая норма.
P>> Например, для MSVC легален следующий код: К>
P>>SomeClass sc(2);
P>>...
P>>someFunc(sc);
P>>if(!sc.isValid())
P>> sc.SomeClass::SomeClass(7); // объявления нет, только определение
P>>otherFunc(sc);
P>>...
К>
К>Повторное конструирование — это UB. Сделать-то ты можешь, но в ряде случаев результат будет разрушительным для работы программы.
P>>В частности, если в SomeClass запрещены копирующий конструктор и "=", то есть проблемы. К>Так бы сразу и сказал, что тебе хочется. К>1) Не используй инициализацию в стиле присваивания. К>
К>SomeClass sc = 123;
К>// требуется, чтобы был доступен конструктор копирования,
К>// т.к. по сути это эквивалентно записи
К>SomeClass sc( SomeClass(123) );
К>// хотя компилятор и вправе сэкономить на копировании
К>SomeClass sc(123); // вуаля!
К>
Тут не совсем понял, что имеется ввиду. Вроде по условию было сказано, что оператор "=" и копирующий конструктор запрещены, а в примере это не так.
К>2) Откажись от повторно-конструируемых переменных в пользу К>- присваивания К>- — оператором = (с учётом всех неявностей) К>- — специальной функцией "swap" (обмен содержимым) — перегрузка std::swap или собственные аналоги К>- — специальной функцией "assign" (явное присваивание), например, у boost::scoped_ptr, где неявный operator= запрещён, есть метод reset; или у std::stream метод rdbuf (назначение streambuf) К>- контейнеров — начиная просто от умных указателей до std::vector, практикующего placement new, или своих рукоделий
Спасибо большое, обязательно посмотрю.
К>Ну а писать К>
К>if(!sc.isValid())
К> new(&sc)SomeClass(7); // стандартная форма, в отличие от ms-specific
К>
К>это однозначно UB. Либо объект в первой строчке не сконструирован — тогда нельзя вызывать его методы; либо он сконструирован, тогда нельзя повторно конструировать.
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, pasenger, Вы писали:
E>>>Обычно в любом случае можно определить (если ещё не определён std::swap)
P>>Не совсем понимаю, что имеется ввиду. В моей версии STL std::swap определен следующим образом P>>и как такой swap способен помочь нашему SomeClass (как было сказано, без "=")? E>Для такого класса надо определить swap как-то иначе. обычно его можно определить, даже если нельзя определить конструктор копирования и оператор присваивания.
E>Смотри: E>
Работает, зараза Фантастика.
E>>>В любом случае твой код неверне потому, что не зовёт деструктор более позднего объекта, и не вовремя зовёт деструктор более раннего... E>смотри, ты создаёшь по одному и тому же адресу два объекта. Один автоматический, а другой по new. E>При этом зовётся деструктор автоматического, но на тот момент там уже лежат данные объекта созданного по new...
Согласен, подозрительно выглядит.
P>>Очень часто можно позволить себе обойтись без вызова деструктора. В противном случае E>1) Не понятно зачем иметь в классе деструктор, без которого можно обойтись? E>Мало того, если класс POD или что-то около того, то можно и std::swap написать просто тупо меняя местами бинарные данные, и оператор присваивания всегда иметь...
P>>
P>> A a(4);
P>> a.~A(); // Так нельзя. Никто не обещал, что так получится
/* нельзя вызывать явно деструктор? */
P>>
Вобщем выглядит все убедительно. Тем не менее, претензия некая к плюсам ощущается. Сформулировать грамотно пока времени нет. И так день убил.
Спасибо. Было полезно и интересно.
Здравствуйте, pasenger, Вы писали:
P>>> a.~A(); // Так нельзя. Никто не обещал, что так получится P>/* нельзя вызывать явно деструктор? */
Нельзя вызывать явно деструктор автоматического объекта.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, MasterZiv, Вы писали:
MZ>Вообще-то я имел в виду, что чел. напишет
MZ> sc.~SomeClass();
MZ> new(&sc)SomeClass(7);
MZ>ТАК можно ? :-P
Так — имхо, можно. Естественно, соблюдая аккуратность.
Но вообще, этот способ совершенно неустойчив против исключений в конструкторе и деструкторе (приведёт к повторному вызову деструктора — на выходе из блока, где определён sc).
Лучше переписать программу так, чтобы не было нужды в переконструировании.
Или, если уж припёрло (и нет возможности для рефакторинга) — сделать контейнер
MZ>>ТАК можно ? :-P
К>Так — имхо, можно. Естественно, соблюдая аккуратность.
Я бы так уверенно не говорил.
Вроде бы нет гарантий никаких, что так можно...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
К>>Так — имхо, можно. Естественно, соблюдая аккуратность. E>Я бы так уверенно не говорил. E>Вроде бы нет гарантий никаких, что так можно...
А я очень неуверенно говорил
На самом деле, танцы с реконструированием объекта — это или свидетельство адски плохого дизайна, или попытка выжать последнюю каплю при ручной оптимизации.
Здравствуйте, Кодт, Вы писали:
К>...или попытка выжать последнюю каплю при ручной оптимизации.
Пример приведи, а то как-то не ясно зачем бы это так делать
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
К>>...или попытка выжать последнюю каплю при ручной оптимизации. E>Пример приведи, а то как-то не ясно зачем бы это так делать
Ну это к автору.
А вот пример (надуманный)
void foo()
{
// создаётся какой-то немеряный локальный контекст
X x;
Y y;
Z z;
// в котором участвует и наша переменная
File f;
f.open( setup1() );
blablabla1();
if( xz1 ) f.reopen( setup2() );
blablabla2();
if( xz2 ) f.reopen( setup3() );
blablabla3();
f.close();
}
Пусть так сложилось, что в API класса File оказалась недоступна отложенная инициализация (open/close). А есть только конструктор
Пришлось рефакторить.
void foo()
{
X x; Y y; Z z;
auto_ptr<File> f;
f.reset(new File(setup1()));
blablabla1();
if( xz ) f.reset(new File(setup2()));
blablabla2();
if( xz ) f.reset(new File(setup3()));
blablabla3();
f.reset(0);
}
Чтобы не копипастить — оформляем фрагменты как функции, и передаём в них контекст.
Смотрим, что нам дешевле — проинлайнить (скопипастить, макросами налепить) или гонять ссылки на контекст по стеку.
Наверно, и то, и другое дорого. И уж точно — заморочно.
Тогда возвращаемся к указателю. Но используем наш родной аллокатор на стеке.
Здравствуйте, Кодт, Вы писали:
К>А вот пример (надуманный)
Прости, но он даже очень надуманный. Теперь вопросы.
К>Пусть так сложилось, что в API класса File оказалась недоступна отложенная инициализация (open/close). А есть только конструктор
А почему это "так случилось"? Зачем это таки надо этому классу? И почему бы не изменить его самого?
Ну положим, что это бибьлиотечнй хмурый класс...
К>Наверно, и то, и другое дорого. И уж точно — заморочно. К>Тогда возвращаемся к указателю. Но используем наш родной аллокатор на стеке.
Ну я бы блочный аллокатор использовал конечно и всё. Скажем вывел бы наследника, с перекрытым new/delete...
Но вообще хотелось бы понять тчо это за объект такой хитрый, что аллокацию на него жалко, а сам его пересоздать трудно...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
К>>А вот пример (надуманный) E>Прости, но он даже очень надуманный. Теперь вопросы.
Ты пытаешься спросить меня, откуда Я возьму такой кривой дизайн? С тем, чтоб потом разгромить, а я буду защищать?
Лично мне неоткуда его взять. Но если у кого-то завалялись макароны в коде — просто как данность... Тогда становятся понятны (хотя и не оправданы) движения в сторону реконструирования объектов.
А ограничения на динамическую память — элементарно: программирование реалтайма, обработчиков прерываний, нулевого кольца, и т.п.
Впрочем, нет! Вспомнил! Есть у меня в проекте кастрюлька с макаронами — поток, разнообразно мониторящий операционку. Там постоянно меняется набор условий мониторинга.
Идея практически та же самая, что и в blablabla/setup/xz, только без ограничений на Assignable.
Смелости не хватает отрефакторить — уж больно кудрявая логика.
Здравствуйте, Кодт, Вы писали:
К>Ты пытаешься спросить меня, откуда Я возьму такой кривой дизайн? С тем, чтоб потом разгромить, а я буду защищать?
Нет, просто мне трудно понять что должно получиться, чтобы такой путь был самым простым
А чем блочный аллокатор не подходит-то во всех случаях?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском