Шаблон декоратора — как мне НЕ вызвать конструктор копирования при инициализации?

У меня есть проблема при попытке использовать шаблон декоратора. Конструкторы распечатывают адреса для отладки. Составлено с:

g++ -g -o go Decorator.cpp

Мой упрощенный код:

#include <iostream>

class Base
{
public:
Base()
{
std::cout << "Base created - this: " << this << std::endl;
}
virtual ~Base() {}
};

class Decorator : public Base
{
public:
Decorator(const Base & decorated)
: _decorated(&decorated)
{
std::cout << "Decorator created - this: " << this << " created - _decorated is " << _decorated << std::endl;
}~Decorator()
{
std::cout << "Decorator destroyed" << std::endl;
std::cout << "  This: " << this << ", _decorated: " << _decorated << std::endl;
}

private:
const Base * _decorated;
};

class Inside : public Base
{
public:
Inside()
{   std::cout << "Inside created - this: " << this << std::endl;   }
};

class Outside : public Decorator
{
public:
Outside(const Base & decorated)
: Decorator(decorated)
{
std::cout << "Outside created - this: " << this << std::endl;
}
};

class Group : public Decorator
{
public:
Group()
: Decorator(_outside)
, _outside(_inside)
{
std::cout << "Group created - this: " << this << std::endl;
}

~Group()
{
std::cout << "Group destroyed" << std::endl;
}

private:
Inside  _inside;
Outside _outside;
};

int main()
{
std::cout << "Hi there" << std::endl;

Group g1;

std::cout << "Done" << std::endl;
}

Моя проблема в Group :: Group (). Я считаю, что инициализация базовой части Decorator группы с неинициализированным _outside — это хорошо — единственное, что нужно Decorator, — это указатель на объект. Моя проблема в том, что Decorator (_outside), кажется, вызывает конструктор копирования, который я НЕ хочу.

Боже мой!

Breakpoint 1, _fu0___ZSt4cout () at Decorator.cpp:63
63          Group g1;
(gdb) print g1
$1 = {<Decorator> = {<Base> = {_vptr.Base = 0x77c34e29},
_decorated = 0x77c34e42}, _inside = warning: can't find linker symbol for vi
rtual table for `Inside' value
{<Base> = {
_vptr.Base = 0x401a90}, <No data fields>},
_outside = {<Decorator> = {<Base> = {_vptr.Base = 0x22ff58},
_decorated = 0x401af6}, <No data fields>}}

Я ломаюсь перед конструктором g1 и записываю пару _decorated членов с известными значениями, чтобы помочь отладке.

(gdb) set g1._decorated = 0
(gdb) set g1._outside._decorated = 0xeeeeeeee
(gdb) print g1
$2 = {<Decorator> = {<Base> = {_vptr.Base = 0x77c34e29}, _decorated = 0x0},
_inside = warning: can't find linker symbol for virtual table for `Inside' val
ue
{<Base> = {_vptr.Base = 0x401a90}, <No data fields>},
_outside = {<Decorator> = {<Base> = {_vptr.Base = 0x22ff58},
_decorated = 0xeeeeeeee}, <No data fields>}}
(gdb) n
Base created - this: 0x22ff34
Inside created - this: 0x22ff34
Base created - this: 0x22ff38
Decorator created - this: 0x22ff38 created - _decorated is 0x22ff34
Outside created - this: 0x22ff38
Group created - this: 0x22ff2c
65          std::cout << "Done" << std::endl;
(gdb) print g1
$3 = {<Decorator> = {<Base> = {_vptr.Base = 0x4042b8},
_decorated = 0xeeeeeeee}, _inside = {<Base> = {
_vptr.Base = 0x4042c8}, <No data fields>},
_outside = {<Decorator> = {<Base> = {_vptr.Base = 0x4042d8},
_decorated = 0x22ff34}, <No data fields>}}

После конструктора g1._decorated имеет неинициализированное значение _outside._decorated в качестве своего _decorated члена, что означает, что был вызван конструктор копирования. Если я добавлю код конструктора копирования в класс Decorator:

Decorator(const Decorator & that)
{   std::cout << "Copy constructor - this: " << this << " - that: " << &that << std::endl;   }

это действительно называет это.

Если я изменю вторую строку конструктора группы с

: Decorator(_outside)

в

: Decorator(static_cast<const Base &>(_outside))

и запустить GDB

Breakpoint 1, _fu0___ZSt4cout () at Decorator.cpp:63
63          Group g1;
(gdb) print g1
$1 = {<Decorator> = {<Base> = {_vptr.Base = 0x77c34e29},
_decorated = 0x77c34e42}, _inside = warning: can't find linker symbol for vi
rtual table for `Inside' value
{<Base> = {
_vptr.Base = 0x401a90}, <No data fields>},
_outside = {<Decorator> = {<Base> = {_vptr.Base = 0x22ff58},
_decorated = 0x401af6}, <No data fields>}}
(gdb) set g1._decorated = 0
(gdb) set g1._outside._decorated = 0xeeeeeeee
(gdb) print g1
$2 = {<Decorator> = {<Base> = {_vptr.Base = 0x77c34e29}, _decorated = 0x0},
_inside = warning: can't find linker symbol for virtual table for `Inside' val
ue
{<Base> = {_vptr.Base = 0x401a90}, <No data fields>},
_outside = {<Decorator> = {<Base> = {_vptr.Base = 0x22ff58},
_decorated = 0xeeeeeeee}, <No data fields>}}
(gdb) n
Base created - this: 0x22ff2c
Decorator created - this: 0x22ff2c created - _decorated is 0x22ff38
Base created - this: 0x22ff34
Inside created - this: 0x22ff34
Base created - this: 0x22ff38
Decorator created - this: 0x22ff38 created - _decorated is 0x22ff34
Outside created - this: 0x22ff38
Group created - this: 0x22ff2c
65          std::cout << "Done" << std::endl;
(gdb) print g1
$3 = {<Decorator> = {<Base> = {_vptr.Base = 0x4042b8},
_decorated = 0x22ff38}, _inside = {<Base> = {
_vptr.Base = 0x4042c8}, <No data fields>},
_outside = {<Decorator> = {<Base> = {_vptr.Base = 0x4042d8},
_decorated = 0x22ff34}, <No data fields>}}

конструктор копирования Decorator НЕ вызывается, и все выглядит хорошо. Мне не нравится это решение, так как оно требует от каждого последующего класса не забывать делать это.

Есть ли способ получить группу из Decorator с членом Decorator и НЕ вызывать конструктор копирования?

1

Решение

Моя проблема в том, что Decorator (_outside), кажется, вызывает конструктор копирования, который я НЕ хочу.

Что вы ожидаете от этого?

Decorator не имеет конструктора, принимающего Outside поэтому подходящие конструкторы:

Decorator(const Base&)

или неявно определенный конструктор копирования:

Decorator(const Decorator&)

Первый вариант предполагает неявное преобразование из Outside в Base в то время как второй включает преобразование из Outside в Decoratorчто является «лучшим» преобразованием, потому что Outside в Base конверсия «проходит» Decorator чтобы добраться до Base,

Как вы обнаружили, чтобы вызвать нужный конструктор, вам нужно явно выполнить желаемое преобразование:

Decorator(static_cast<Base&>(_outside))

Это необходимо, потому что тип, который вы передаете на самом деле Decorator поэтому, конечно, он предпочитает конструктор копирования.

Другое решение состоит в том, чтобы добавить конструктор, который будет использоваться вместо конструктора копирования, например, шаблон, соответственно ограниченный:

template<typename T>
Decorator(const T& decorated, typename boost::enable_if<boost::is_base_of<T, Base> >* = 0)
: _decorated(&decorated)
{ }

Это будет использоваться для всего, что происходит от Base но это не Base и не является Decorator

В C ++ 11 вы можете сделать это немного чище

template<typename T,
typename Requires = typename std::enable_if<std::is_base_of<T, Base>::value>>
Decorator(const T& decorated)
: _decorated(&decorated)
{ }
1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector