С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 20.01.14 01:39
Оценка:
Скажем есть указатель на уже выделенную память под объект.
Как вызвать конструктор по этому месту?

Вариант с placement new я знаю:

void *pmem = ...;
Thing* pt = new(pmem) Thing(params);


Это единственный вариант?
Re: С++, как вызвать конструктор объекта по месту?
От: jazzer Россия Skype: enerjazzer
Дата: 20.01.14 03:29
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Скажем есть указатель на уже выделенную память под объект.

CS>Как вызвать конструктор по этому месту?

CS>Вариант с placement new я знаю:


CS>
CS>void *pmem = ...;
CS>Thing* pt = new(pmem) Thing(params);
CS>


CS>Это единственный вариант?


Да, это именно он и есть, а чем он не устраивает?
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: С++, как вызвать конструктор объекта по месту?
От: jyuyjiyuijyu  
Дата: 20.01.14 03:44
Оценка: 42 (1)
Здравствуйте, c-smile, Вы писали:

CS>Это единственный вариант?


а так не ?

((ololo*)p)->ololo::ololo();
Re[2]: С++, как вызвать конструктор объекта по месту?
От: Alexander G Украина  
Дата: 20.01.14 08:09
Оценка: 43 (2)
Здравствуйте, jyuyjiyuijyu, Вы писали:

J>
J>((ololo*)p)->ololo::ololo();
J>


Так только в MSVC
Русский военный корабль идёт ко дну!
Re[2]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 20.01.14 17:04
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Да, это именно он и есть, а чем он не устраивает?


Мне нужно решить на самом деле обратную задачу
Вызвать placement new без вызова конструктора.

Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует)
vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.

Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing
Re[3]: С++, как вызвать конструктор объекта по месту?
От: night beast СССР  
Дата: 20.01.14 17:18
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Мне нужно решить на самом деле обратную задачу

CS>Вызвать placement new без вызова конструктора.

CS>Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует)

CS>vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.

дык это, может мемкопи первых нескольких байт?

CS>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing
Re[4]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 20.01.14 17:27
Оценка:
Здравствуйте, night beast, Вы писали:

NB>дык это, может мемкопи первых нескольких байт?


a. Хотелось бы переносимое решение. Это компилируется в MSVC, Apple XCode и GCC.
b. Если с "to" можно еще как-то разобраться, то что делать с from? Откуда копировать vtbl ?
Re[5]: С++, как вызвать конструктор объекта по месту?
От: night beast СССР  
Дата: 20.01.14 17:33
Оценка:
Здравствуйте, c-smile, Вы писали:

NB>>дык это, может мемкопи первых нескольких байт?


CS>a. Хотелось бы переносимое решение. Это компилируется в MSVC, Apple XCode и GCC.


общая пустая база есть?

CS>b. Если с "to" можно еще как-то разобраться, то что делать с from? Откуда копировать vtbl ?


не подумал
Re[5]: С++, как вызвать конструктор объекта по месту?
От: night beast СССР  
Дата: 20.01.14 19:37
Оценка:
Здравствуйте, c-smile, Вы писали:

NB>>дык это, может мемкопи первых нескольких байт?


CS>a. Хотелось бы переносимое решение. Это компилируется в MSVC, Apple XCode и GCC.

CS>b. Если с "to" можно еще как-то разобраться, то что делать с from? Откуда копировать vtbl ?

существующий код менять нельзя?
может что-то вроде этого
Автор: alnsn
Дата: 02.05.04
подойдет?
Re[6]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 20.01.14 22:01
Оценка:
Здравствуйте, night beast, Вы писали:

NB>существующий код менять нельзя?


Можно менять если что-то значительно лучше.

NB>может что-то вроде этого
Автор: alnsn
Дата: 02.05.04
подойдет?


Я солидарен с кодт: "Сделали всё то же, что делает компилятор, только руками."
Как-то не радует руками прописывать vtbl. В реальности там что-то под 50 методов. Да еще и super calls используются.
Re[3]: С++, как вызвать конструктор объекта по месту?
От: jazzer Россия Skype: enerjazzer
Дата: 21.01.14 00:56
Оценка: +2 :)
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, jazzer, Вы писали:


J>>Да, это именно он и есть, а чем он не устраивает?


CS>Мне нужно решить на самом деле обратную задачу

CS>Вызвать placement new без вызова конструктора.

placement new без вызова конструктора называется reinterpret_cast

CS>Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует)

CS>vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.

CS>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing


Для произвольного левого класса это невозможно сделать, если ты хочешь, чтоб код оставался соответствующим Стандарту. Если классы все под твоим контролем — юзай указатель pimpl, в него ты можешь засунуть все, что угодно (в т.ч. std::string), и не инициализировать, если он уже есть (ну или передавать указатель в конструктор, чтоб уж совсем не зависеть от его точного расположения в классе).

Ну и по-хорошему ты еще должен корректно прибить тот объект, который там лежал, вызвав его деструктор (хотя с точки зрения lifetime это и не обязательно — достаточно факта переиспользования памяти).
struct Thing
{
  struct ThingImpl;
  typedef std::unique_ptr<ThingImpl> Pimpl;

  Thing(Pimpl p) : pimpl_(p) {}
  // другие нормальные инициализирующие pimpl_ конструкторы

  virtual ~Thing() {} // pimpl_ будет прибит тут автоматом

  template<class T>
  T* mutate() {
    Pimpl tmp( pimpl_ ); // теперь this своим pimpl-ом не владеет
    ~Thing(); // естественно, деструктор должен быть виртуальным
    return new(this) T(tmp);
  }

protected: // наследникам нужен доступ к потрохам
  Pimpl pimpl_;
};
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[7]: С++, как вызвать конструктор объекта по месту?
От: night beast СССР  
Дата: 21.01.14 02:43
Оценка:
Здравствуйте, c-smile, Вы писали:

NB>>может что-то вроде этого
Автор: alnsn
Дата: 02.05.04
подойдет?


CS>Я солидарен с кодт: "Сделали всё то же, что делает компилятор, только руками."


все так. но к этой vtbl ты имеешь доступ.

CS>Как-то не радует руками прописывать vtbl. В реальности там что-то под 50 методов. Да еще и super calls используются.


может есть возможность отделить данные от интерфейса. через предложенный Pimp или что-то вроде optional'а сделать:

struct data {};

struct test {

   optional< data > m_data;

   test ( NO_INIT ) { m_data.is_valid = true; }

};
Re[4]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 21.01.14 05:51
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, c-smile, Вы писали:


CS>>Здравствуйте, jazzer, Вы писали:


J>>>Да, это именно он и есть, а чем он не устраивает?


CS>>Мне нужно решить на самом деле обратную задачу

CS>>Вызвать placement new без вызова конструктора.

J>placement new без вызова конструктора называется reinterpret_cast


Ну нет конечно. placement new как минимум инициализирует vtbl если она есть. reinterpret_cast он ничего не делает. Или я не понял сермяжность.

CS>>Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует)

CS>>vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.

CS>>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing


J>Для произвольного левого класса это невозможно сделать, если ты хочешь, чтоб код оставался соответствующим Стандарту. Если классы все под твоим контролем — юзай указатель pimpl, в него ты можешь засунуть все, что угодно (в т.ч. std::string), и не инициализировать, если он уже есть (ну или передавать указатель в конструктор, чтоб уж совсем не зависеть от его точного расположения в классе).


pimpl это a) лишний redirection level и б) потребность указания одних и тех же функций два раза.

Собственно vtbl и есть pimpl если смотреть на абстрактный класс и его специализацию/имплементацию.

J>Ну и по-хорошему ты еще должен корректно прибить тот объект, который там лежал, вызвав его деструктор (хотя с точки зрения lifetime это и не обязательно — достаточно факта переиспользования памяти).


Не надо там ничего прибивать. Ни до ни после turn_thing_to.
Поля остаются теми же и по тому же offsetof.
C++ ведь разрешает работать с конструкциями:
union {
  Thing t1;
  OtherThing t2;
}

мой вариант это то же самое только несколько иначе описанный.
Re[5]: С++, как вызвать конструктор объекта по месту?
От: jazzer Россия Skype: enerjazzer
Дата: 21.01.14 06:38
Оценка: 27 (2)
Здравствуйте, c-smile, Вы писали:

CS>>>Вызвать placement new без вызова конструктора.


J>>placement new без вызова конструктора называется reinterpret_cast


CS>Ну нет конечно. placement new как минимум инициализирует vtbl если она есть. reinterpret_cast он ничего не делает. Или я не понял сермяжность.


Это делает конструктор. Вернее, стандарт не специфицирует vtbl вообще, но по сути этим занимается конструктор в качестве первого шага, так как указатель на vtbl — это просто скрытый член, который надо проинициализировать.

Так что сермяжность очень простая: new выделяет память, вызывает конструктор, возвращает указатель нужного типа.
placement new убирает первый шаг, оставляя вызов конструктора и возврат указателя нужного типа.
Если ты убираешь и конструктор, остается просто возврат указателя — это reinterpret_cast.

CS>>>Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует)

CS>>>vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.

CS>>>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing


J>>Для произвольного левого класса это невозможно сделать, если ты хочешь, чтоб код оставался соответствующим Стандарту. Если классы все под твоим контролем — юзай указатель pimpl, в него ты можешь засунуть все, что угодно (в т.ч. std::string), и не инициализировать, если он уже есть (ну или передавать указатель в конструктор, чтоб уж совсем не зависеть от его точного расположения в классе).


CS>pimpl это a) лишний redirection level и б) потребность указания одних и тех же функций два раза.

CS>Собственно vtbl и есть pimpl если смотреть на абстрактный класс и его специализацию/имплементацию.

Да, это pimpl таблицы методов. Но тебе ж еще и данные нужны

J>>Ну и по-хорошему ты еще должен корректно прибить тот объект, который там лежал, вызвав его деструктор (хотя с точки зрения lifetime это и не обязательно — достаточно факта переиспользования памяти).


CS>Не надо там ничего прибивать. Ни до ни после turn_thing_to.

CS>Поля остаются теми же и по тому же offsetof.
Ты можешь это гарантировать? И в случае множественного наследования тоже?
Для начала, размещение полей вообще никак не специфицировано, если тип не standard layout (а это как раз твой случай).

Так что ты уж определись — либо ты хочешь иметь код, соответствующий стандарту (как, например, код, который я привел), либо код, который "работает на доступных мне компиляторах".
Если второе — да, можешь предполагать что угодно, и проще всего вообще будет тупо вломиться в объект и поменять его внутренний указатель на vtbl, нужную тебе, и не заморачиваться с конструкторами, placement new и прочим — дешево и сердито.

CS>C++ ведь разрешает работать с конструкциями:

CS>
CS>union {
CS>  Thing t1;
CS>  OtherThing t2;
CS>} 
CS>

CS>мой вариант это то же самое только несколько иначе описанный.

Очень ограниченно разрешает. Ты не можешь записать в t1, а потом прочитать из t2. Либо одно, либо другое. Хочешь поменять — прибей одно, создай другое, читай [class.union]:

4 [ Note: In general, one must use explicit destructor calls and placement new operators to change the active
member of a union
. —end note ] [ Example: Consider an object u of a union type U having non-static data
members m of type M and n of type N. If M has a non-trivial destructor and N has a non-trivial constructor
(for instance, if they declare or inherit virtual functions), the active member of u can be safely switched
from m to n using the destructor and placement new operator as follows:

u.m.~M();
new (&u.n) N;
—end example ]

С объединениями из standard layout типов правила немного слабее, но это не твой случай из-за виртуальностей.
Если ты собираешься писать standard-compliant код, конечно же.

Если хочешь — сделай явный указатель на таблицу методов, сохраняя свои классы standard layout, и меняй потом эту таблицу руками как обычный член — с этим проблем нет.
Но если ты хочешь пользоваться встроенной системой типов С++ в соответствии со стандартом — придется соответствовать, не предполагая, в частности, никаких vtbl и прочего.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[6]: С++, как вызвать конструктор объекта по месту?
От: rusted Беларусь  
Дата: 21.01.14 07:42
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Если второе — да, можешь предполагать что угодно, и проще всего вообще будет тупо вломиться в объект и поменять его внутренний указатель на vtbl, нужную тебе, и не заморачиваться с конструкторами, placement new и прочим — дешево и сердито.


Только нет гарантий, что это будет работать всегда. Например, при оптимизации компилятор может в каком-то месте определить, что тип объекта известен, и просто принлайнить вызовы некоторых виртуальных функций. И придется долго вылавливать почему на некоторых сборках с оптимизацией что-то работает не так.
Re[3]: С++, как вызвать конструктор объекта по месту?
От: k.o. Россия  
Дата: 21.01.14 09:08
Оценка: 27 (2)
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, jazzer, Вы писали:


J>>Да, это именно он и есть, а чем он не устраивает?


CS>Мне нужно решить на самом деле обратную задачу

CS>Вызвать placement new без вызова конструктора.

CS>Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует)

CS>vtbl. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.

CS>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing


Может имеет смысл вынести данные из Thing?

struct Data
{
    int a;
};

struct Storage
{
    // здесь, возможно, будет лучше использовать aligned_storage для Data и Thing/OtherThing/YetAnotherThing.
    Data data;
    Thing thing;
};


Если создавать Thing только как часть Storage, вполне можно будет вычислять смещение Data относительно Thing::this в compile-time, т.е. можно будет сделать что-то вроде

class Thing
{
    ...
    Data *data()
    {
        return ((char *)this) + offset;
    }
    ...

    virtual int value() { return data()->a; }
};


Тогда при пересоздании Thing, данные, гарантировано не изменятся и скорость работы с ними должна быть такой же, как если бы они лежали в Thing.
Re[7]: С++, как вызвать конструктор объекта по месту?
От: cserg  
Дата: 21.01.14 09:23
Оценка: +1
Здравствуйте, c-smile, Вы писали:

Если boost перевариваете, то можно использовать aligned_storage.

#include <iostream>
#include <string>
#include "boost/aligned_storage.hpp"

using namespace std;

struct NO_INIT {};

struct ThingData {
    int a;
    string s;
    ThingData(int v): a (v), s("test") { std::cout << "create ThingData\n"; }
    ~ThingData() { std::cout << "destroy ThingData\n"; }
};

class Thing {
    typedef ThingData data_type;
    typedef boost::aligned_storage<sizeof(data_type),
            boost::alignment_of<data_type>::value>::type storage_type;
    storage_type storage;

protected:
    Thing(NO_INIT) {}
    data_type* get_data() {return reinterpret_cast<data_type*>(&storage);}
    const data_type* get_data() const {return reinterpret_cast<const data_type*>(&storage);}

public:
    Thing(int v = 0) { new (&storage) data_type(v); }
    virtual ~Thing() { (*get_data()).~data_type(); }

    virtual const char * type_name(){ return "Thing"; }
    virtual int value() const { return get_data()->a; }
    virtual string str() const { return get_data()->s; }
};


Если boost не нравится, то можно выдернуть из LLVM похожий класс. Называется AlignedCharArrayUnion, содержится в llvm/Support/AlignOf.h
Re[8]: С++, как вызвать конструктор объекта по месту?
От: night beast СССР  
Дата: 21.01.14 09:27
Оценка:
Здравствуйте, cserg, Вы писали:

C>Если boost перевариваете, то можно использовать aligned_storage.


+1.

C>
C>#include <iostream>
C>#include <string>
C>#include "boost/aligned_storage.hpp"

C>


осталось сделать следующий щаг: завернуть это в класс, и получим что-то типа optional, только без доп.мембера.
Re[3]: С++, как вызвать конструктор объекта по месту?
От: Кодт Россия  
Дата: 21.01.14 12:14
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing


А если не выклёвывать себе мозг всякими трюками, и сделать по-честному:
struct Operand;

// ООП (паттерн Стратегия)
struct IOperator
{
  virtual void doit(Operand& operand) const = 0;
  virtual const char* whoami() const = 0;
};

// Си или ФП
struct OperatorVTBL
{
  void (*doit)(Operand& operand);
  const char* (*whoami)();
  const char* itsme;
};

struct Operand
{
  int f1, f2;
  IOperator* op; // так
  OperatorVTBL* vp; // этак

  void doit() { op->doit(*this);  }
  const char* whoami() const { return op->whoami(); }
};

struct Summator : IOperator
{
  void doit(Operand& operand) { operand.f1 += operand.f2; }
  const char* whoami() const { return "+"; }
} summator_v1;

OperatorVTBL summator_v2 = {
  [](Operator& operand)->void { operand.f1 += operand.f2; },
  []()->const char* { return "+"; },
  "+",
};

Сишный способ даёт на одну косвенность меньше. А если в интерфейсе только одна функция, то можно вообще сразу эту функцию присваивать (предельный случай идиомы NVI).


Ну и наконец, маленький, но странный хак. Срезка наооборот.
#include <cstdio>
#include <memory>

struct Base
{
    int x, y, z;
    Base(int i) : x(i), y(i+i), z(i*i) {}
    virtual void whoami() { printf("%p base %d %d %d\n", this, x, y, z); }
};

struct Derived : Base
{
    Derived(Base&& b) : Base(b) {}
    virtual void whoami() { printf("%p derived %d %d %d\n", this, x, y, z); }
};

int main()
{
    Base b(3);
    Base* p = &b;

    b.whoami();   // 000000000022fd50 base 3 6 9
    p->whoami();  // 000000000022fd50 base 3 6 9

    Base t(std::move(b));
    Derived* d = new(&b)Derived(std::move(t));

    printf("-----\n");
    b.whoami();   // 000000000022fd50 base 3 6 9
    p->whoami();  // 000000000022fd50 derived 3 6 9
    d->whoami();  // 000000000022fd50 derived 3 6 9
};

Обрати внимание, что компилятор не ждёт подлянки с подменой типа и немножко оптимизирует.
Перекуём баги на фичи!
Re[3]: С++, как вызвать конструктор объекта по месту?
От: Evgeny.Panasyuk Россия  
Дата: 21.01.14 16:47
Оценка: 4 (1)
Здравствуйте, c-smile, Вы писали:

CS>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing


SO.
Можно использовать boost::variant для меняющихся частей. Например:
struct ThingData
{
    int f1;
    int f2;
};

struct Summator
{
    void doAction1(ThingData &self)  { self.f1 += self.f2; }
    const char* type_name() { return "Summator"; }
};

struct Substractor
{
    void doAction1(ThingData &self)  { self.f1 -= self.f2; }
    const char* type_name() { return "Substractor"; }
};

using Thing = SubVariant<ThingData, Summator, Substractor>;

int main()
{
    auto test = [](auto &self, auto &sub)
    {
        sub.doAction1(self);
        cout << sub.type_name() << " " << self.f1 << " " << self.f2 << endl;
    };

    Thing x = {{5, 7}, Summator{}};
    apply(test, x);
    x.sub = Substractor{};
    apply(test, x);

    cout << "size: " << sizeof(x.sub) << endl;
}
Вывод:
Summator 12 7
Substractor 5 7
size: 2

  full code
#define BOOST_VARIANT_MINIMIZE_SIZE

#include <boost/variant.hpp>
#include <type_traits>
#include <functional>
#include <iostream>
#include <utility>

using namespace std;

/****************************************************************/
// Boost.Variant requires result_type:
template<typename T, typename F>
struct ResultType
{
     mutable F f;
     using result_type = T;

     template<typename ...Args> T operator()(Args&& ...args) const
     {
         return f(forward<Args>(args)...);
     }
};

template<typename T, typename F>
auto make_result_type(F &&f)
{
    return ResultType<T, typename decay<F>::type>{forward<F>(f)};
}
/****************************************************************/
// Proof-of-Concept
template<typename Base, typename ...Ts>
struct SubVariant
{
    Base shared_data;
    boost::variant<Ts...> sub;

    template<typename Visitor>
    friend auto apply(Visitor visitor, SubVariant &operand)
    {
        using result_type = typename common_type
        <
            decltype( visitor(shared_data, declval<Ts&>()) )...
        >::type;

        return boost::apply_visitor(make_result_type<result_type>([&](auto &x)
        {
            return visitor(operand.shared_data, x);
        }), operand.sub);
    }
};
/****************************************************************/
// Demo:

struct ThingData
{
    int f1;
    int f2;
};

struct Summator
{
    void doAction1(ThingData &self)  { self.f1 += self.f2; }
    const char* type_name() { return "Summator"; }
};

struct Substractor
{
    void doAction1(ThingData &self)  { self.f1 -= self.f2; }
    const char* type_name() { return "Substractor"; }
};

using Thing = SubVariant<ThingData, Summator, Substractor>;

int main()
{
    auto test = [](auto &self, auto &sub)
    {
        sub.doAction1(self);
        cout << sub.type_name() << " " << self.f1 << " " << self.f2 << endl;
    };
    
    Thing x = {{5, 7}, Summator{}};
    apply(test, x);
    x.sub = Substractor{};
    apply(test, x);

    cout << "size: " << sizeof(x.sub) << endl;
}
Re[4]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 21.01.14 17:05
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, c-smile, Вы писали:


CS>>Контекст проблемы: http://stackoverflow.com/questions/21212379/changing-vtbl-of-existing-object-on-the-fly-dynamic-subclassing


К>А если не выклёвывать себе мозг всякими трюками, и сделать по-честному:


"По честному" это писать VTBL руками. С сигнатурами функций типа doIt(Data* _this, ) и пр.
В итоге получишь то что компилятор C++ и так делает, как ты сам сказал когда-то.

Вот про это

Base t(std::move(b));
Derived* d = new(&b)Derived(std::move(t));


я думал. Попробуй в members описать std::string поле.

Короче вместо Base(NO_INIT) конструкторов тебе нужно будет писать Base(Base &&moved) конструкторы. Ровно столько же.
И это всегда будет требовать temporary при конвертации и ненулевой CPU penalty.
Re[6]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 21.01.14 17:35
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Здравствуйте, c-smile, Вы писали:


CS>>>>Вызвать placement new без вызова конструктора.


J>>>placement new без вызова конструктора называется reinterpret_cast


CS>>Ну нет конечно. placement new как минимум инициализирует vtbl если она есть. reinterpret_cast он ничего не делает. Или я не понял сермяжность.


J>Это делает конструктор. Вернее, стандарт не специфицирует vtbl вообще, но по сути этим занимается конструктор в качестве первого шага, так как указатель на vtbl — это просто скрытый член, который надо проинициализировать.


Это происходит между new и первым конструктором. Но это не суть важно.

J>Так что сермяжность очень простая: new выделяет память, вызывает конструктор, возвращает указатель нужного типа.

J>placement new убирает первый шаг, оставляя вызов конструктора и возврат указателя нужного типа.
J>Если ты убираешь и конструктор, остается просто возврат указателя — это reinterpret_cast.

Ну ок. "Рука бога" вставляет vtbl pointer.
Для простоты будем считать что кроме VTBL никакой другой практической имплементации виртуальности нам не известно.
Во всяком случае для всех компиляторов поддерживающих COM и CORBA объекты из коробки это именно VTBL и ничто другое.

J>>>Для произвольного левого класса это невозможно сделать, если ты хочешь, чтоб код оставался соответствующим Стандарту. Если классы все под твоим контролем — юзай указатель pimpl, в него ты можешь засунуть все, что угодно (в т.ч. std::string), и не инициализировать, если он уже есть (ну или передавать указатель в конструктор, чтоб уж совсем не зависеть от его точного расположения в классе).


Ищется решение которое вводит наиментшее кол-во сущностей и CPU penalty.

J>Но если ты хочешь пользоваться встроенной системой типов С++ в соответствии со стандартом — придется соответствовать, не предполагая, в частности, никаких vtbl и прочего.


Странно, все говорят про Стандарт, но никто так конкретно и не говорит чем мое решение его нарушает.

Вообще какая-то странная ситуация получается. С одной стороны использование reinterpret_cast конструкций типа как легитимно в C++.
А с другой стороны вариация оного так сразу супротив Стандарта.
Re[7]: С++, как вызвать конструктор объекта по месту?
От: cserg  
Дата: 21.01.14 18:22
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Ищется решение которое вводит наиментшее кол-во сущностей и CPU penalty.


Ну давайте, тогда объясняйте чем решение через aligned_storage не подходит.
Re[5]: С++, как вызвать конструктор объекта по месту?
От: Кодт Россия  
Дата: 21.01.14 19:41
Оценка: 42 (1) +1
Здравствуйте, c-smile, Вы писали:

К>>А если не выклёвывать себе мозг всякими трюками, и сделать по-честному:

CS>"По честному" это писать VTBL руками. С сигнатурами функций типа doIt(Data* _this, ) и пр.
CS>В итоге получишь то что компилятор C++ и так делает, как ты сам сказал когда-то.

А ты бы лучше рассказал, зачем тебе все эти танцы с бубном.
Возможно, что у надзадачи есть изящные решения, не связанные с тем, как обхитрить ООП.


CS>Вот про это


CS>
CS>Base t(std::move(b));
CS>Derived* d = new(&b)Derived(std::move(t));
CS>


CS>я думал. Попробуй в members описать std::string поле.


Попробовал, у меня всё заработало.
  Скрытый текст
#include <iostream>
#include <string>
#include <memory>

struct A
{
    int x;
    std::string y;
    A(int x, std::string y) : x(x), y(y) {}

    virtual const char* who() const { return "A"; }
    void show() const { std::cout << (void const*)this << " " << who() << " " << x << " [" << y << "]" << std::endl; }
};

struct B : A
{
    virtual const char* who() const { return "B"; }
    B(A&& a) : A(std::move(a)) {}
};

int main()
{
    A a(123, "mama");
    a.show(); // 0xbfbefa58 A 123 [mama]

    A t(std::move(a));
    a.show(); // 0xbfbefa58 A 123 []
    t.show(); // 0xbfbefa64 A 123 [mama]
    new(&a)B(std::move(t));
    a.show(); // 0xbfbefa58 B 123 [mama]
    t.show(); // 0xbfbefa64 A 123 []
}


CS>Короче вместо Base(NO_INIT) конструкторов тебе нужно будет писать Base(Base &&moved) конструкторы. Ровно столько же.

CS>И это всегда будет требовать temporary при конвертации и ненулевой CPU penalty.

Это, по крайней мере, — в рамках языка. Хоть и UB.
А если добавить перед new(&a) вызов a.~A() — так и вообще станет похоже на использование aligned_storage.
Хакать же все vfptr'ы — непереносимое решение, требующее знания лэяута конкретных классов на конкретной платформе.

Нулевой штраф производительности — это паттерны Стратегия и Состояние.
На NVI там даже лишней косвенности не возникнет, если тебя волнует штраф производительности при дальнейшей работе...
Перекуём баги на фичи!
Re[7]: С++, как вызвать конструктор объекта по месту?
От: Кодт Россия  
Дата: 21.01.14 19:53
Оценка:
Здравствуйте, c-smile, Вы писали:

J>>Если ты убираешь и конструктор, остается просто возврат указателя — это reinterpret_cast.

CS>Ну ок. "Рука бога" вставляет vtbl pointer.

Вот чтобы специально расстроить, напомню: в классе может быть много vfptr'ов.
У первой полиморфной базы — с нулевым смещением; у последующих полиморфных баз; ну и у членов-данных, но они при подмене типа не поменяются.

CS>Для простоты будем считать что кроме VTBL никакой другой практической имплементации виртуальности нам не известно.

CS>Во всяком случае для всех компиляторов поддерживающих COM и CORBA объекты из коробки это именно VTBL и ничто другое.

NVI — рукодельщина из коробки.
Если хорошенько шаблонно поколдовать, то можем и автоматизировать её.

Сразу вспоминаются shared_ptr, function и any.

J>>Но если ты хочешь пользоваться встроенной системой типов С++ в соответствии со стандартом — придется соответствовать, не предполагая, в частности, никаких vtbl и прочего.

CS>Странно, все говорят про Стандарт, но никто так конкретно и не говорит чем мое решение его нарушает.

Ну как что нарушает. Вагон неопределённого поведения.

Хирургия с подменой vfptr — это частный случай стрельбы по памяти.

placement new — инвалидирует старый объект, ну и что, что почти такой же новый по тому же адресу возник. Как мы видели, компилятор может надеяться на то, что если объект валидный, то и его тип неизменный и известный заранее, и сэкономить на виртуальных вызовах.

CS>Вообще какая-то странная ситуация получается. С одной стороны использование reinterpret_cast конструкций типа как легитимно в C++.

CS>А с другой стороны вариация оного так сразу супротив Стандарта.

Стандарт разрешает стрелять в ногу, но говорит, что скорую помощь и патологоанатома будут оплачивать жертва и её родственники.
Перекуём баги на фичи!
Re[8]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 21.01.14 21:31
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, c-smile, Вы писали:


J>>>Если ты убираешь и конструктор, остается просто возврат указателя — это reinterpret_cast.

CS>>Ну ок. "Рука бога" вставляет vtbl pointer.

К>Вот чтобы специально расстроить, напомню: в классе может быть много vfptr'ов.

К>У первой полиморфной базы — с нулевым смещением; у последующих полиморфных баз; ну и у членов-данных, но они при подмене типа не поменяются.

Нет там полиморфных баз по условию задачи. Еще раз: рассматривается конкретная задача а не конь в ваакуме.

J>>>Но если ты хочешь пользоваться встроенной системой типов С++ в соответствии со стандартом — придется соответствовать, не предполагая, в частности, никаких vtbl и прочего.

CS>>Странно, все говорят про Стандарт, но никто так конкретно и не говорит чем мое решение его нарушает.

К>Ну как что нарушает. Вагон неопределённого поведения.


"Имя, сестра, имя!" Что конкретно неопределено?

new(place), использование конструкторов или что?

К>Хирургия с подменой vfptr — это частный случай стрельбы по памяти.


Где я там меняю vfptr?

К>placement new — инвалидирует старый объект, ну и что, что почти такой же новый по тому же адресу возник. Как мы видели, компилятор может надеяться на то, что если объект валидный, то и его тип неизменный и известный заранее, и сэкономить на виртуальных вызовах.


Не может он там экономить. Обращение всегда идет через pointer на базу (Thing).

CS>>Вообще какая-то странная ситуация получается. С одной стороны использование reinterpret_cast конструкций типа как легитимно в C++.

CS>>А с другой стороны вариация оного так сразу супротив Стандарта.

К>Стандарт разрешает стрелять в ногу, но говорит, что скорую помощь и патологоанатома будут оплачивать жертва и её родственники.


Николай, давай на технических деталях сфокусируемся, лирику люди поют в политике.
Re[9]: С++, как вызвать конструктор объекта по месту?
От: niXman Ниоткуда https://github.com/niXman
Дата: 21.01.14 21:36
Оценка:
Здравствуйте, c-smile, Вы писали:

К>>placement new — инвалидирует старый объект, ну и что, что почти такой же новый по тому же адресу возник. Как мы видели, компилятор может надеяться на то, что если объект валидный, то и его тип неизменный и известный заранее, и сэкономить на виртуальных вызовах.


CS>Не может он там экономить.


ошибаетесь. GCC и Clang успешно справляются с двевертуализацией там, где она не нужна.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[6]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 21.01.14 21:38
Оценка:
Здравствуйте, Кодт, Вы писали:

CS>>Короче вместо Base(NO_INIT) конструкторов тебе нужно будет писать Base(Base &&moved) конструкторы. Ровно столько же.

CS>>И это всегда будет требовать temporary при конвертации и ненулевой CPU penalty.

К>Это, по крайней мере, — в рамках языка. Хоть и UB.

К>А если добавить перед new(&a) вызов a.~A() — так и вообще станет похоже на использование aligned_storage.

В чем там UB?

К>Хакать же все vfptr'ы — непереносимое решение, требующее знания лэяута конкретных классов на конкретной платформе.


Да не хакаю я vfptr'ы там нигде! В том то и point. По месту одного объекта создается другой. В чем проблема-то?

К>Нулевой штраф производительности — это паттерны Стратегия и Состояние.

К>На NVI там даже лишней косвенности не возникнет, если тебя волнует штраф производительности при дальнейшей работе...
Re[6]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 21.01.14 22:31
Оценка:
Здравствуйте, Кодт, Вы писали:

Вот твоя идея, но немного доработанная: http://ideone.com/5O6SFV
Re[10]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 22.01.14 00:16
Оценка:
Здравствуйте, niXman, Вы писали:

X>Здравствуйте, c-smile, Вы писали:


К>>>placement new — инвалидирует старый объект, ну и что, что почти такой же новый по тому же адресу возник. Как мы видели, компилятор может надеяться на то, что если объект валидный, то и его тип неизменный и известный заранее, и сэкономить на виртуальных вызовах.


CS>>Не может он там экономить.


X>ошибаетесь. GCC и Clang успешно справляются с двевертуализацией там, где она не нужна.


Если обращаться derived.virtualMethod() то да, может.
Но base->virtualMethod() — это совсем другая песня.
Re[11]: С++, как вызвать конструктор объекта по месту?
От: k.o. Россия  
Дата: 22.01.14 07:34
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Здравствуйте, niXman, Вы писали:


X>>Здравствуйте, c-smile, Вы писали:


К>>>>placement new — инвалидирует старый объект, ну и что, что почти такой же новый по тому же адресу возник. Как мы видели, компилятор может надеяться на то, что если объект валидный, то и его тип неизменный и известный заранее, и сэкономить на виртуальных вызовах.


CS>>>Не может он там экономить.


почему нет?

X>>ошибаетесь. GCC и Clang успешно справляются с двевертуализацией там, где она не нужна.


CS>Если обращаться derived.virtualMethod() то да, может.

CS>Но base->virtualMethod() — это совсем другая песня.

Другая будет если менять base после создания нового объекта

base = turn_A_to<B>(base);

иначе будет UB, компилятор имеет право считать, что vfptr не поменялся и вызывать методы для старого типа.
Re[11]: С++, как вызвать конструктор объекта по месту?
От: Tilir Россия http://tilir.livejournal.com
Дата: 22.01.14 08:12
Оценка: -1
Здравствуйте, c-smile, Вы писали:

CS>Если обращаться derived.virtualMethod() то да, может.

CS>Но base->virtualMethod() — это совсем другая песня.

То не подумать ли ещё раз над другой песней?

#include <iostream>

struct A {
    virtual void func (void) {std::cout << "A\n";};
};

struct B : A {
    virtual void func (void) {std::cout << "B\n";}
};

static int
bar (B *b)
{
  b->func(); //this will be inlined in optimized builds.
}

int
main (void)
{
  B b;
  bar (&b);
  return 0;
}


Компилируем gcc 4.8.1 c -O2, смотрим на сгенерированный код

main:
subq $24, %rsp
movq %rsp, %rdi
movq $_ZTV1B+16, (%rsp)
call _ZN1B4funcEv
... etc

_ZN1B4funcEv:
movl $2, %edx
movl $.LC0, %esi
movl $_ZSt4cout, %edi
jmp _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
... etc

.LC0:
.string "B\n"

Re[12]: С++, как вызвать конструктор объекта по месту?
От: k.o. Россия  
Дата: 22.01.14 08:57
Оценка:
Здравствуйте, Tilir, Вы писали:

T>Здравствуйте, c-smile, Вы писали:


CS>>Если обращаться derived.virtualMethod() то да, может.

CS>>Но base->virtualMethod() — это совсем другая песня.

T>То не подумать ли ещё раз над другой песней?


T>Компилируем gcc 4.8.1 c -O2, смотрим на сгенерированный код

  Скрытый текст
T>

T>main:
T> subq $24, %rsp
T> movq %rsp, %rdi
T> movq $_ZTV1B+16, (%rsp)
T> call _ZN1B4funcEv
T> ... etc

T>_ZN1B4funcEv:
T> movl $2, %edx
T> movl $.LC0, %esi
T> movl $_ZSt4cout, %edi
T> jmp _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_l
T> ... etc

T>.LC0:
T> .string "B\n"



Это ничего не доказывает. Ну получили мы после оптимизации код, который ведет себя также как неоптимизированный, ну и что? Нужен пример, для которого оптимизация изменит поведение. А в таком тривиальном случае, компилятор заинлайнит виртуальные вызовы даже после "смены типа", причем сделает это правильно:

int
main (void)
{
  B b;
  bar (&b);
  ::new (&b) A;
  bar (&b);
  return 0;
}


в первом случае будет B::f(), а после "смены типа" A::f().
Re[9]: С++, как вызвать конструктор объекта по месту?
От: Кодт Россия  
Дата: 22.01.14 09:35
Оценка: +1
Здравствуйте, c-smile, Вы писали:

CS>Нет там полиморфных баз по условию задачи. Еще раз: рассматривается конкретная задача а не конь в ваакуме.


Полиморфная база там есть. (В противовес виртуальной базе и просто базе без виртуальных методов).
Конкретная задача озвучена тобой немножко в вакууме. Понятно, что ты пытаешься гоняться за производительностью, но непонятно, в каком масштабе и вообще зачем.

К>>Ну как что нарушает. Вагон неопределённого поведения.


CS>"Имя, сестра, имя!" Что конкретно неопределено?

CS>new(place), использование конструкторов или что?

Я уже сказал: пропатчивание объекта. Ты или стреляешь по памяти, или реконструируешь объект, тем самым инвалидируя предыдущий.

К>>Хирургия с подменой vfptr — это частный случай стрельбы по памяти.

CS>Где я там меняю vfptr?

Твои слова или не твои?

Мне нужно решить на самом деле обратную задачу
Вызвать placement new без вызова конструктора.

Для классов с виртуальными функциями new(oldobj) просто меняет (инициализирует)
vtbl
. А конструктор инициализирует поля. Мне надо вызвать new(place) но не конструктор.


К>>placement new — инвалидирует старый объект, ну и что, что почти такой же новый по тому же адресу возник. Как мы видели, компилятор может надеяться на то, что если объект валидный, то и его тип неизменный и известный заранее, и сэкономить на виртуальных вызовах.


CS>Не может он там экономить. Обращение всегда идет через pointer на базу (Thing).


Вообще-то, может.
Попробуй скомпилировать вот это на g++ 4.7 с параметрами -O2 и -O0, увидишь разницу.
#include <cstdio>
#include <memory>

struct Base
{
    virtual const char* whoami() const { return "Base"; }
};

struct Derived : Base
{
    Derived(Base&& b) : Base(b) {}
    virtual const char* whoami() const { return "Derived"; }
};

void patch(Base* b)
{
    Base t(std::move(*b));
    Derived* d = new(b)Derived(std::move(t));    
}

int main()
{
    Base ob;
    Base* pb = &ob;
    patch(pb);
    printf("%s %s\n", ob.whoami(), pb->whoami()); // -O2: Base Base | -O0: Base Derived
};

MSVC, кстати, даже в самых агрессивных режимах оптимизации форсирует виртуальный вызов от указателя/ссылки, даже если ему не давать повод что-либо заподозрить (закомментировать patch).

К>>Стандарт разрешает стрелять в ногу, но говорит, что скорую помощь и патологоанатома будут оплачивать жертва и её родственники.

CS>Николай, давай на технических деталях сфокусируемся, лирику люди поют в политике.

Технические детали — это твоё право делать всё, что угодно well-formed, но некоторые WF программы ведут к undefined behavior, а UB бывает предсказуемым и непредсказуемым, желаемым и нежелаемым.


И кстати, чтобы два раза не вставать.
На всякий случай напоминаю, что
— placement new operator вообще ничего не делает, а new expression вызывает конструктор. Нет такой штуки "вызвать предконструктор, но свалить из тела конструктора". Даже если с помощью исключения мы поймаем момент, когда vfptr уже настроен, а до конструирования членов ещё не дошли, — всё равно, будут проблемы.
— vfptr в ходе конструирования наследника настраивается много раз, по мере прохождения конструкторов баз — в отличие от дотнета, жабы и дельфей.
Перекуём баги на фичи!
Re[7]: С++, как вызвать конструктор объекта по месту?
От: Кодт Россия  
Дата: 22.01.14 09:36
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Вот твоя идея, но немного доработанная: http://ideone.com/5O6SFV


Выстави агрессивную оптимизацию — и обломишься.
Перекуём баги на фичи!
Re[7]: С++, как вызвать конструктор объекта по месту?
От: jazzer Россия Skype: enerjazzer
Дата: 22.01.14 10:25
Оценка:
Здравствуйте, c-smile, Вы писали:

J>>Это делает конструктор. Вернее, стандарт не специфицирует vtbl вообще, но по сути этим занимается конструктор в качестве первого шага, так как указатель на vtbl — это просто скрытый член, который надо проинициализировать.


CS>Это происходит между new и первым конструктором. Но это не суть важно.


То же самое надо сделать и при размещении объекта на стеке, и при инициализации каждой базы в цепочке баз. Нафига компилятору заниматься магией "между", если по сути это просто инициализация скрытого члена? Вот писал бы ты компилятор и тебе понадобилось бы вставить в объект скрытый член — ты бы как поступил? Вставил бы код его инициализации первой строчкой кода конструктора, чтоб потом просто этот конструктор звать в одну инструкцию? Или перед каждым вызовом конструктора впихивал бы код инициализации? С нулевой экономией причем — так как конструктор все равно потом звать надо.

J>>>>Для произвольного левого класса это невозможно сделать, если ты хочешь, чтоб код оставался соответствующим Стандарту. Если классы все под твоим контролем — юзай указатель pimpl, в него ты можешь засунуть все, что угодно (в т.ч. std::string), и не инициализировать, если он уже есть (ну или передавать указатель в конструктор, чтоб уж совсем не зависеть от его точного расположения в классе).


CS>Ищется решение которое вводит наиментшее кол-во сущностей и CPU penalty.


Простое разделение данных и виртуальной иерархии кода, который с этими данными работает — достаточно "наименьшее"?
Тебе ведь по сути это надо — менять обработчик, не трогая данные?
Ну так раздели их полностью.

Вообще, тебя ведь несколько раз попросили уже объяснить свою реальную задачу. Ради чего все эти пляски с подменами?

J>>Но если ты хочешь пользоваться встроенной системой типов С++ в соответствии со стандартом — придется соответствовать, не предполагая, в частности, никаких vtbl и прочего.


CS>Странно, все говорят про Стандарт, но никто так конкретно и не говорит чем мое решение его нарушает.

"твое решение" — это какое? С объединением — я процитировал стандарт. Исходное — ничего не нарушает (разве что ты не должен пользоваться старым указателем, только новым, которые возвращен из turn_thing_to), но кривое и не работает со сколь-нибудь интересными типами (ты и сам про это пишешь).

CS>Вообще какая-то странная ситуация получается. С одной стороны использование reinterpret_cast конструкций типа как легитимно в C++.

Оно очень условно легитимно. Прочитай соответствующий раздел стандарта. По сути вообще нет никаких гарантий со стороны стандарта относительно того, что вернет reinterpret_cast, за исключением очень небольшого количества оговоренных случаев.

CS>А с другой стороны вариация оного так сразу супротив Стандарта.

UB — оно в соответствии со стандартом UB, совсем не супротив
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[10]: С++, как вызвать конструктор объекта по месту?
От: k.o. Россия  
Дата: 22.01.14 10:46
Оценка:
Здравствуйте, Кодт, Вы писали:

К>>>placement new — инвалидирует старый объект, ну и что, что почти такой же новый по тому же адресу возник. Как мы видели, компилятор может надеяться на то, что если объект валидный, то и его тип неизменный и известный заранее, и сэкономить на виртуальных вызовах.


CS>>Не может он там экономить. Обращение всегда идет через pointer на базу (Thing).


К>Вообще-то, может.

К>Попробуй скомпилировать вот это на g++ 4.7 с параметрами -O2 и -O0, увидишь разницу.
  Скрытый текст
К>
К>#include <cstdio>
К>#include <memory>

К>struct Base
К>{
К>    virtual const char* whoami() const { return "Base"; }
К>};

К>struct Derived : Base
К>{
К>    Derived(Base&& b) : Base(b) {}
К>    virtual const char* whoami() const { return "Derived"; }
К>};

К>void patch(Base* b)
К>{
К>    Base t(std::move(*b));
К>    Derived* d = new(b)Derived(std::move(t));    
К>}

К>int main()
К>{
К>    Base ob;
К>    Base* pb = &ob;
К>    patch(pb);
К>    printf("%s %s\n", ob.whoami(), pb->whoami()); // -O2: Base Base | -O0: Base Derived
К>};
К>

К>MSVC, кстати, даже в самых агрессивных режимах оптимизации форсирует виртуальный вызов от указателя/ссылки, даже если ему не давать повод что-либо заподозрить (закомментировать patch).

А не бага-ли это в GCC? Даже если вместо patсh(pb) написать pb = new(pb)Derived; GCC все-равно использует "Base" к качестве результата pb->whoami().

int main()
{
    void *pmem = nullptr;
    Base *pb = nullptr;
    
    Derived o1;
    pmem = &o1;
    pb = new(pmem)Base;
    printf("%s\n", pb->whoami()); // ok: -O2: Base | -O0: Base
    
    Base o2;
    pmem = &o2;
    pb = new(pmem)Derived;
    printf("%s\n", pb->whoami()); // failed: -O2: Base | -O0: Derived
}


Что интересно, это происходит только если использовать память объекта созданного на стеке.

    Base *pb = new Base;
    pb = new(pb)Derived;
    printf("%s\n", pb->whoami()); // ok: -O2: Derived| -O0: Derived
Re[11]: С++, как вызвать конструктор объекта по месту?
От: Кодт Россия  
Дата: 22.01.14 11:50
Оценка:
Здравствуйте, k.o., Вы писали:

KO>А не бага-ли это в GCC? Даже если вместо patсh(pb) написать pb = new(pb)Derived; GCC все-равно использует "Base" к качестве результата pb->whoami().


Вообще какая-то муть. На -O2 получаем
int main()
{
    Derived o1;
    Base* pbb = new(&o1)Base();
    Base* pb1 = &o1;
    printf("\n");
    printf("%s\n", pb1->whoami()); // Base
    printf("%s\n", pbb->whoami()); // Base
    
    Base o2;
    Derived* pbd = new(&o2)Derived();
    Derived* pb2 = (Derived*)&o2;
    printf("\n");
    printf("%s\n", pb2->whoami()); // Base
    printf("%s\n", pbd->whoami()); // Base

    Base* o3 = &o1;
    Derived* pbm = new(o3)Derived();
    Base* pb3 = (Base*)o3;
    printf("\n");
    printf("%s\n", pb3->whoami()); // Derived
    printf("%s\n", pbm->whoami()); // Derived

    Base* o4 = &o2;
    Derived* pbn = new(o4)Derived();
    Base* pb4 = (Base*)o4;
    printf("\n");
    printf("%s\n", pb4->whoami()); // Base
    printf("%s\n", pbn->whoami()); // Base

    Base o5[1]; // или любое другое хранилище, например, intptr_t[12345]
    Derived* pbz = new(o5)Derived();
    Base* pb5 = (Base*)o5;
    printf("\n");
    printf("%s\n", pb5->whoami()); // Derived
    printf("%s\n", pbz->whoami()); // Derived
};


А вот интересно, это баг или неопределённое поведение. Думаю, что всё же второе, потому что мы конструируем объект поверх существующего, лежащего в автоматическом или статическом хранилище (для последнего — нужно o1-o5 вынести из функции или снабдить static'ами). Безотносительно того, будем ли предварительно инвалидировать объекты, вызывая у них деструкторы.
Ну не предназначены именованные объекты для того, чтобы над ними творили такую жестокость.
Перекуём баги на фичи!
Re[12]: С++, как вызвать конструктор объекта по месту?
От: k.o. Россия  
Дата: 22.01.14 12:39
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, k.o., Вы писали:


KO>>А не бага-ли это в GCC? Даже если вместо patсh(pb) написать pb = new(pb)Derived; GCC все-равно использует "Base" к качестве результата pb->whoami().


К>Вообще какая-то муть. На -O2 получаем

  Скрытый текст
К>
К>int main()
К>{
К>    Derived o1;
К>    Base* pbb = new(&o1)Base();
К>    Base* pb1 = &o1;
К>    printf("\n");
К>    printf("%s\n", pb1->whoami()); // Base
К>    printf("%s\n", pbb->whoami()); // Base
    
К>    Base o2;
К>    Derived* pbd = new(&o2)Derived();
К>    Derived* pb2 = (Derived*)&o2;
К>    printf("\n");
К>    printf("%s\n", pb2->whoami()); // Base
К>    printf("%s\n", pbd->whoami()); // Base

К>    Base* o3 = &o1;
К>    Derived* pbm = new(o3)Derived();
К>    Base* pb3 = (Base*)o3;
К>    printf("\n");
К>    printf("%s\n", pb3->whoami()); // Derived
К>    printf("%s\n", pbm->whoami()); // Derived

К>    Base* o4 = &o2;
К>    Derived* pbn = new(o4)Derived();
К>    Base* pb4 = (Base*)o4;
К>    printf("\n");
К>    printf("%s\n", pb4->whoami()); // Base
К>    printf("%s\n", pbn->whoami()); // Base

К>    Base o5[1]; // или любое другое хранилище, например, intptr_t[12345]
К>    Derived* pbz = new(o5)Derived();
К>    Base* pb5 = (Base*)o5;
К>    printf("\n");
К>    printf("%s\n", pb5->whoami()); // Derived
К>    printf("%s\n", pbz->whoami()); // Derived
К>};
К>

К>А вот интересно, это баг или неопределённое поведение. Думаю, что всё же второе, потому что мы конструируем объект поверх существующего, лежащего в автоматическом или статическом хранилище (для последнего — нужно o1-o5 вынести из функции или снабдить static'ами). Безотносительно того, будем ли предварительно инвалидировать объекты, вызывая у них деструкторы.

В стандарте я на это запрета не видел, при этом там подробно описывается в каких случаях такое использование storage автоматических объектов приведет к UB, соответственно в прочих случаях все должно работать.

К>Ну не предназначены именованные объекты для того, чтобы над ними творили такую жестокость.


А как же, например, какой-нибудь там, aligned_storage?
Re[13]: С++, как вызвать конструктор объекта по месту?
От: Кодт Россия  
Дата: 22.01.14 12:54
Оценка:
Здравствуйте, k.o., Вы писали:

К>>А вот интересно, это баг или неопределённое поведение. Думаю, что всё же второе, потому что мы конструируем объект поверх существующего, лежащего в автоматическом или статическом хранилище (для последнего — нужно o1-o5 вынести из функции или снабдить static'ами). Безотносительно того, будем ли предварительно инвалидировать объекты, вызывая у них деструкторы.


KO>В стандарте я на это запрета не видел, при этом там подробно описывается в каких случаях такое использование storage автоматических объектов приведет к UB, соответственно в прочих случаях все должно работать.


Возможно, что это ещё и дефект стандарта.
Ну вот не привиделось комитетчикам в страшном сне такое надругательство.

К>>Ну не предназначены именованные объекты для того, чтобы над ними творили такую жестокость.


KO>А как же, например, какой-нибудь там, aligned_storage?


aligned_storage — это хорошо выравненный POD. Его вообще можно memset'ить как угодно, — почему бы не использовать и для placement new.
Перекуём баги на фичи!
Re[14]: С++, как вызвать конструктор объекта по месту?
От: k.o. Россия  
Дата: 22.01.14 13:30
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, k.o., Вы писали:


К>>>А вот интересно, это баг или неопределённое поведение. Думаю, что всё же второе, потому что мы конструируем объект поверх существующего, лежащего в автоматическом или статическом хранилище (для последнего — нужно o1-o5 вынести из функции или снабдить static'ами). Безотносительно того, будем ли предварительно инвалидировать объекты, вызывая у них деструкторы.


KO>>В стандарте я на это запрета не видел, при этом там подробно описывается в каких случаях такое использование storage автоматических объектов приведет к UB, соответственно в прочих случаях все должно работать.


К>Возможно, что это ещё и дефект стандарта.

К>Ну вот не привиделось комитетчикам в страшном сне такое надругательство.

Да ну, откуда тогда взялся, например, 3.8/8?

If a program ends the lifetime of an object of type T with static (3.7.1), thread (3.7.2), or automatic (3.7.3)
storage duration and if T has a non-trivial destructor,39 the program must ensure that an object of the
original type occupies that same storage location when the implicit destructor call takes place; otherwise the
behavior of the program is undefined. This is true even if the block is exited with an exception. [ Example:

class T { };
struct B {
    ~B();
};

void h() {
    B b;
    new (&b) T;
} // undefined behavior at block exit

—end example ]



К>>>Ну не предназначены именованные объекты для того, чтобы над ними творили такую жестокость.


KO>>А как же, например, какой-нибудь там, aligned_storage?


К>aligned_storage — это хорошо выравненный POD. Его вообще можно memset'ить как угодно, — почему бы не использовать и для placement new.


Все равно это тоже объект или ты имел в виду объект в смысле ООП?
Re[15]: С++, как вызвать конструктор объекта по месту?
От: Кодт Россия  
Дата: 22.01.14 14:03
Оценка:
Здравствуйте, k.o., Вы писали:

К>>Ну вот не привиделось комитетчикам в страшном сне такое надругательство.

KO>Да ну, откуда тогда взялся, например, 3.8/8?

А, ну значит, привиделось. Но сон был достаточно страшным, чтобы они в иллюстрации вместо упомянутого типа T написали B, а T задействовали для хака.
  Скрытый текст
KO>

If a program ends the lifetime of an object of type T...

KO>class T { };
KO>struct B {
KO>    ~B();
KO>};

KO>void h() {
KO>    B b;
KO>    new (&b) T;
KO>} // undefined behavior at block exit

Приду домой, попробую тщательно воскурить, какие именно апокалиптические видения там есть.
Ибо, одно видение "деструктор старого объекта после переконструирования" уже упомянуто.
Но это же обязательное, но не исчерпывающее условие.


К>>>>Ну не предназначены именованные объекты для того, чтобы над ними творили такую жестокость.

KO>>>А как же, например, какой-нибудь там, aligned_storage?
К>>aligned_storage — это хорошо выравненный POD. Его вообще можно memset'ить как угодно, — почему бы не использовать и для placement new.
KO>Все равно это тоже объект или ты имел в виду объект в смысле ООП?

В том смысле, в каком слово "объект" используется в стандарте, а не в смысле ООП.
Перекуём баги на фичи!
Re[16]: С++, как вызвать конструктор объекта по месту?
От: k.o. Россия  
Дата: 22.01.14 14:14
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, k.o., Вы писали:


К>>>Ну вот не привиделось комитетчикам в страшном сне такое надругательство.

KO>>Да ну, откуда тогда взялся, например, 3.8/8?

К>А, ну значит, привиделось. Но сон был достаточно страшным, чтобы они в иллюстрации вместо упомянутого типа T написали B, а T задействовали для хака.

К>
  Скрытый текст
KO>>

If a program ends the lifetime of an object of type T...

К>
KO>>class T { };
KO>>struct B {
KO>>    ~B();
KO>>};

KO>>void h() {
KO>>    B b;
KO>>    new (&b) T;
KO>>} // undefined behavior at block exit
К>


К>Приду домой, попробую тщательно воскурить, какие именно апокалиптические видения там есть.
К>Ибо, одно видение "деструктор старого объекта после переконструирования" уже упомянуто.
К>Но это же обязательное, но не исчерпывающее условие.

Ну да, это только один пример, остальные условия тоже в 3.8 описываются, но, на мой взгляд, ни одно из них не влечет UB в обсуждаемом случае.

К>>>>>Ну не предназначены именованные объекты для того, чтобы над ними творили такую жестокость.

KO>>>>А как же, например, какой-нибудь там, aligned_storage?
К>>>aligned_storage — это хорошо выравненный POD. Его вообще можно memset'ить как угодно, — почему бы не использовать и для placement new.
KO>>Все равно это тоже объект или ты имел в виду объект в смысле ООП?

К>В том смысле, в каком слово "объект" используется в стандарте, а не в смысле ООП.


тогда aligned_storage тоже объект
Re[17]: С++, как вызвать конструктор объекта по месту?
От: Кодт Россия  
Дата: 22.01.14 14:49
Оценка:
Здравствуйте, k.o., Вы писали:

К>>В том смысле, в каком слово "объект" используется в стандарте, а не в смысле ООП.

KO>тогда aligned_storage тоже объект

У aligned_storage нет виртуальных методов. Его сперва реинтерпретируют.
Перекуём баги на фичи!
Re[8]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 22.01.14 16:49
Оценка:
Здравствуйте, jazzer, Вы писали:

J>Вообще, тебя ведь несколько раз попросили уже объяснить свою реальную задачу. Ради чего все эти пляски с подменами?


Я объяснил на SO:

To AntonTykhyy: consider tree of HTML DOM elements (class element). After parsing and later in runtime each DOM element may get different layout models defined by CSS/styles, script, etc. So each element is allowed to be of class block_element, table_element, etc — with different layout and visibility rules. Having such dynamic subclassing is essential to architecture that I use in my Sciter engine : terrainformatica.com/sciter – c-smile 17 hours ago

To c-smile: Then change the architecture. If you want to change elements' behavior at runtime, separate behavior from data and assign pointers to behavior objects. The only thing you are saving using this sort of undefined behavior is one pointer access, and one very likely to hit the cache, at that. – Anton Tykhyy 17 hours ago

To AntonTykhyy: Additional level of indirection is one drawback, another one is that use of strategy or pimpl pattern increases maintenance/development cost. Root class has around 50 methods that need to be bridged to current strategy methods. Not that pretty at the end. And it's not clear why to introduce new entities if C++ already has builtin infrastructure. Virtuality is a strategy implementation if to think slightly out of the box. c-smile 16 hours ago


Также я привел вариацию решения Кодта: http://ideone.com/5O6SFV
которое (решение) в принципе удовлетворяет условию задачи. Я так UB там и не увидел. Но я и в своем решении проблемы не вижу, кстати.
И его и мое решение в принципе делают одно и то же. Просто решение Николая позволяет использовать объекты из std например в качестве members.
Но ценой создания temporary variable, move ctors и вызова dtor'а который опять же ничего не делает кроме исполнения ненужного кода.
Но так как я там std контейнеры/строки не использую и все нужные объекты имеют NO_INIT ctors то я остаюсь при своих пока ибо это наиболее
эффективный вариант поддерживаемый всеми target компиляторами.
Re[9]: С++, как вызвать конструктор объекта по месту?
От: night beast СССР  
Дата: 22.01.14 18:08
Оценка: +1
Здравствуйте, c-smile, Вы писали:

CS>Также я привел вариацию решения Кодта: http://ideone.com/5O6SFV

CS>которое (решение) в принципе удовлетворяет условию задачи. Я так UB там и не увидел. Но я и в своем решении проблемы не вижу, кстати.
CS>И его и мое решение в принципе делают одно и то же. Просто решение Николая позволяет использовать объекты из std например в качестве members.
CS>Но ценой создания temporary variable, move ctors и вызова dtor'а который опять же ничего не делает кроме исполнения ненужного кода.
CS>Но так как я там std контейнеры/строки не использую и все нужные объекты имеют NO_INIT ctors то я остаюсь при своих пока ибо это наиболее
CS>эффективный вариант поддерживаемый всеми target компиляторами.

чисто из любопытства, у тебя какие-нибудь тесты на скорость есть?
хотелось бы сравнить производительность с aligned_storage.
Re[5]: С++, как вызвать конструктор объекта по месту?
От: Erop Россия  
Дата: 22.01.14 18:13
Оценка:
Здравствуйте, c-smile, Вы писали:


CS>a. Хотелось бы переносимое решение. Это компилируется в MSVC, Apple XCode и GCC.

CS>b. Если с "to" можно еще как-то разобраться, то что делать с from? Откуда копировать vtbl ?

А может устроит иметь в одном объекте сразу несколько баз, и "переключать" поведение объекта, просто беря указатели на разные базы?
Ну, скажем что-то типа http://www.rsdn.ru/forum/cpp/2864921
Автор: Erop
Дата: 06.03.08
не подойдёт?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: С++, как вызвать конструктор объекта по месту?
От: Erop Россия  
Дата: 22.01.14 18:26
Оценка:
Здравствуйте, c-smile, Вы писали:


CS>Также я привел вариацию решения Кодта: http://ideone.com/5O6SFV

CS>которое (решение) в принципе удовлетворяет условию задачи. Я так UB там и не увидел. Но я и в своем решении проблемы не вижу, кстати.
CS>И его и мое решение в принципе делают одно и то же. Просто решение Николая позволяет использовать объекты из std например в качестве members.
CS>Но ценой создания temporary variable, move ctors и вызова dtor'а который опять же ничего не делает кроме исполнения ненужного кода.
CS>Но так как я там std контейнеры/строки не использую и все нужные объекты имеют NO_INIT ctors то я остаюсь при своих пока ибо это наиболее
CS>эффективный вариант поддерживаемый всеми target компиляторами.

А ты измерял, или, хотя бы, оценивал, какие выигрыши/проигрыши по производительности имеются в виду?..
Главный недостаток твоего решения -- его очень большая нетрадиционность и отсутствие гарантий. Обычно это влечёт дорогую поддержку.
Во-первых, другие пользователи, и соавторы этого кода, включая тебя, через несколько лет, могут что-то напортачить просто потому, что упустят какую-то тонкость и поступят шаблонно.
Во-вторых, может измениться компилятор, а так, как твой код очень нетипичный, то на таких примерах компилятор скорее всего будет плохо тестироваться.
В общем, при появлении всяких мутных проблем, ,ты никогда не будешь до конца уверен, это твоё решение рассыпалось, или в чём-то ином беда.
Оно выглядит очень хрупким. Соответственно не понятно, ради чего все эти жертвы-то?

Кстати, я не знаю, был ли тут такой вариант, но я так понял, что ты хочешь отделить данные от поведения, но сделать это всё хаком и гарантировать его работоспособность путём соглашений изложенных в доках/комментах?

По идее, можно же это дело разделить более явно, и дать больше гарантий чисто формальных.
Смотри, я так понял, что данные у тебя либо POD, либо перемещаемые. Можно сделать POD-структуры с данными для узлов, а полиморфные объекты, задающие собственно поведение, сделать шаблонами классов, без доп. данных, например выведенных их этих POD'ов, или просто параметризованных ими.
Тогда можно к этой конструёвине приписать шаблонный конструктор перемещения и функцию, которая мувит из одного шаблона в буфер, потом из буфера обратно, но уже в иной шаблон. По идее у компилятора будет шанс это всё оптимизироать, но решение с выравненным хранилищем, всё равно лучше, на мой взгляд...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: С++, как вызвать конструктор объекта по месту?
От: cserg  
Дата: 22.01.14 19:34
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Но так как я там std контейнеры/строки не использую и все нужные объекты имеют NO_INIT ctors то я остаюсь при своих пока ибо это наиболее эффективный вариант поддерживаемый всеми target компиляторами.


Вариант с aligned_storage по скорости такой же как ваш, но позволяет использовать std контейнеры/строки и не требует NO_INIT ctors для членов Thing.
Re[10]: С++, как вызвать конструктор объекта по месту?
От: Evgeny.Panasyuk Россия  
Дата: 22.01.14 19:49
Оценка: +1
Здравствуйте, cserg, Вы писали:

C>Вариант с aligned_storage по скорости такой же как ваш, но позволяет использовать std контейнеры/строки и не требует NO_INIT ctors для членов Thing.


Вариант с boost::variant — требует всего одного switch для целой пачки действий, и никаких virtual calls для каждого отдельного действия:
// boost::variant
apply([](auto &self, auto &sub) // single dispatch per batch
{
    for(int i=0; i!=1000000; ++i)
    {
        sub.doAction1(self); // no dispatch, inlining
        sub.doAction2(self); // no dispatch, inlining
        // ...
    }
}, x);
// versus vtbl
for(int i=0; i!=1000000; ++i)
{
    x->doAction1(); // virtual call (PGO?)
    x->doAction2(); // virtual call (PGO?)
    // ...
}
Re[11]: С++, как вызвать конструктор объекта по месту?
От: night beast СССР  
Дата: 22.01.14 19:57
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

C>>Вариант с aligned_storage по скорости такой же как ваш, но позволяет использовать std контейнеры/строки и не требует NO_INIT ctors для членов Thing.


EP>Вариант с boost::variant — требует всего одного switch для целой пачки действий, и никаких virtual calls для каждого отдельного действия:


все бы ничего, да только варианту все возможные типы предоставить надо.
Re[12]: С++, как вызвать конструктор объекта по месту?
От: Evgeny.Panasyuk Россия  
Дата: 22.01.14 20:27
Оценка:
Здравствуйте, night beast, Вы писали:

EP>>Вариант с boost::variant — требует всего одного switch для целой пачки действий, и никаких virtual calls для каждого отдельного действия:

NB>все бы ничего, да только варианту все возможные типы предоставить надо.

Есть такой момент.
(одним из типов конечно можно сделать какой-нибудь type-erasure "variant<Concrete1, Concrete2, TypeErased>", но тогда соответственно для объектов которые не попадают в список конкретных типов будет overhead аналогичный virtual-call)
Re[6]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 23.01.14 02:34
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, c-smile, Вы писали:



CS>>a. Хотелось бы переносимое решение. Это компилируется в MSVC, Apple XCode и GCC.

CS>>b. Если с "to" можно еще как-то разобраться, то что делать с from? Откуда копировать vtbl ?

E>А может устроит иметь в одном объекте сразу несколько баз, и "переключать" поведение объекта, просто беря указатели на разные базы?

E>Ну, скажем что-то типа http://www.rsdn.ru/forum/cpp/2864921
Автор: Erop
Дата: 06.03.08
не подойдёт?..


Такое не подходит, производных классов достаточно много, что-то около 20. Да и где-то дискриминатор нужно хранить про то какой у него тип сейчас.
Re[10]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 23.01.14 03:41
Оценка:
Здравствуйте, cserg, Вы писали:

C>Здравствуйте, c-smile, Вы писали:


CS>>Но так как я там std контейнеры/строки не использую и все нужные объекты имеют NO_INIT ctors то я остаюсь при своих пока ибо это наиболее эффективный вариант поддерживаемый всеми target компиляторами.


C>Вариант с aligned_storage по скорости такой же как ваш, но позволяет использовать std контейнеры/строки и не требует NO_INIT ctors для членов Thing.


Этот вариант http://ideone.com/5O6SFV тоже не требует NO_INIT, но требует moving ctors правда.
Потребность писать data()->parent вместо parent конечно напрягает но есои прижмет так наверное придется и делать.

Но основная проблема — создание одного объекта по месту другого. Это то что вызывает вопросы у общественности.
Re[11]: С++, как вызвать конструктор объекта по месту?
От: night beast СССР  
Дата: 23.01.14 04:02
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>>>Но так как я там std контейнеры/строки не использую и все нужные объекты имеют NO_INIT ctors то я остаюсь при своих пока ибо это наиболее эффективный вариант поддерживаемый всеми target компиляторами.


C>>Вариант с aligned_storage по скорости такой же как ваш, но позволяет использовать std контейнеры/строки и не требует NO_INIT ctors для членов Thing.


CS>Этот вариант http://ideone.com/5O6SFV тоже не требует NO_INIT, но требует moving ctors правда.

CS>Потребность писать data()->parent вместо parent конечно напрягает но есои прижмет так наверное придется и делать.

так лучше: data->parent?

CS>Но основная проблема — создание одного объекта по месту другого. Это то что вызывает вопросы у общественности.


boost::optional так работает, и особых вопросов по этому поводу ни у кого нет. там это правда внутрь упрятано, но суть от этого не меняется.
Re[11]: С++, как вызвать конструктор объекта по месту?
От: andrew.f  
Дата: 24.01.14 05:14
Оценка:
Здравствуйте, c-smile, Вы писали:

CS>Этот вариант http://ideone.com/5O6SFV тоже не требует NO_INIT, но требует moving ctors правда.

CS>Потребность писать data()->parent вместо parent конечно напрягает но есои прижмет так наверное придется и делать.

CS>Но основная проблема — создание одного объекта по месту другого. Это то что вызывает вопросы у общественности.


А почему бы просто не разбить все это на составные части.
Данные — в одном объекте (чтобы создались один раз и не копировались туда-сюда), виртуальные методы в другом (чтобы перекрывать функционнальность), не-виртуальные в третьем (чтобы везде был доступ через один объект).
И все общение через третий объект, который является фасадом для первых двух.

Ниже грубый пример (в нем не учтен ряд ситуаций), который демонстрирует примерный подход:

#include <iostream>
#include <string>
#include <memory>

struct I
{
    virtual const char* who() const = 0;
};

struct D
{
    int x;
    std::string y;
    D(int x, std::string y) : x(x), y(y) {}
    D(D&& d) : x(std::move(d.x)), y(std::move(d.y)) {}
};

struct A: I
{
    std::unique_ptr< D > data;

    template <typename ...T>
    A(T &&...t) : data(new D(std::forward<T>(t)...)) {}
    A(A &a) { data.swap(a.data); }
    A(A &&a) : data(std::move(a.data)) {}

    const char* who() const { return "A"; }
};

struct B : A
{
    B(A &a) : A(a) {}
    B(A &&a) : A(std::move(a)) {}
    const char* who() const { return "B"; }
};

struct F: I
{
    std::unique_ptr< A >  impl;

    template < typename T >
    F(T &&t) : impl(new T(std::move(t))) {}
    ~F() { }

    const char* who() const { return impl->who(); }

    void show() const { std::cout << (void const*)impl.get() << " " << who() << " " << impl->data->x << " [" <<  impl->data->y << "]" << std::endl; }

    template < typename T>
    void turn_to()
    {
        std::unique_ptr< A > nimpl(new T(*impl.get()));
        impl.swap(nimpl);
    }
};

int main()
{
    std::unique_ptr< F > pa(new F(A(123, "text")));
    pa->show();
    pa->turn_to<B>();
    pa->show();
}
Re[12]: С++, как вызвать конструктор объекта по месту?
От: c-smile Канада http://terrainformatica.com
Дата: 25.01.14 03:21
Оценка:
Здравствуйте, andrew.f, Вы писали:

AF>Здравствуйте, c-smile, Вы писали:


CS>>Этот вариант http://ideone.com/5O6SFV тоже не требует NO_INIT, но требует moving ctors правда.

CS>>Потребность писать data()->parent вместо parent конечно напрягает но есои прижмет так наверное придется и делать.

CS>>Но основная проблема — создание одного объекта по месту другого. Это то что вызывает вопросы у общественности.


AF>А почему бы просто не разбить все это на составные части.

AF>Данные — в одном объекте (чтобы создались один раз и не копировались туда-сюда), виртуальные методы в другом (чтобы перекрывать функционнальность), не-виртуальные в третьем (чтобы везде был доступ через один объект).
AF>И все общение через третий объект, который является фасадом для первых двух.

Это уже обсуждалось.
Вкратце: strategy/pimpl patterns (твой случай) используют дополнительную косвенность. Т.е. за для достать данные нужно два раза перейти по адресу.
Это если рассматривать только технически-эффективную сторону проблемы. Обращение к методам и данных очень частое в данной задаче.

C++ объект с линейной виртуальностью (один предок) представляется как-то так (YMMV):

struct vobject 
{
  vtbl* methods;
  ... data ...
}


т.е. при определенных условиях и аккуратном обращении dynamic subclassing в архитектуру C++ встроен и так.
Вот это я и использую меняя значение поля methods.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.