Выбор конструктора для не копируемого объекта

Предположим, у меня есть не копируемый класс с несколькими конструкторами с таким

class Foo: boost::noncopyable
{
public:
Foo(std::string s) {...};  // construct one way
Foo(int i) {...};  // construct another way
}

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

Я мог бы сделать это с помощью указателей, как это: —

boost::shared_ptr<Foo> f;

if (condition)
f.reset(new Foo(myString));
else
f.reset(new Foo(myInteger));

// common code follows
f->doSomethingComplicated(...);

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


Еще несколько деталей: Foo Класс, приведенный выше, просто иллюстрирует проблему. Фактический класс — Windows Gdiplus::Bitmaphttp://msdn.microsoft.com/en-gb/library/windows/desktop/ms534420(v=vs.85).aspx

12

Решение

Вы можете сделать это с C ++ 11 в стеке, без размещения нового и без копирования / перемещения конструктора / назначения. Заметим:

auto factory = [&]() -> Foo
{
if (condition) {
return { myString };
} else {
return { myInteger };
}
};
Foo&& foo = factory();
foo.doSomethingComplicated();

Функтор и экземпляр Foo будут счастливо жить в стеке, выделения не производятся (кроме копирования строки в Fooконструктор, наверное). Foo вызовет свой деструктор, когда выйдет из области видимости. Выиграть.

При использовании равномерной инициализации для создания возвращаемого значения, операция копирования / перемещения не выполняется. Обратите внимание, что возвращение myString (неявное преобразование) или Foo(myString) заставит компилятор проверить, является ли объект копируемым / перемещаемым, даже если такое копирование / перемещение может быть исключено.

Изменить: в качестве альтернативы, это может быть сделано еще короче, но немного более «волшебным»:

Foo&& foo = [&]() -> Foo
{
if (condition) {
return { myString };
} else {
return { myInteger };
}
}();

Изменить: Pre-C ++ 11, Visual Studio хакерское решение:

VC, похоже, не проверяет, является ли объект копируемым при обращении к конструкторам неявного преобразования для возврата значения из функции. Так что это действительно, даже если это противоречит стандарту:

Foo make_foo(bool condition, const std::string&s, int i)
{
if ( condition) {
return s;
} else {
return i;
}
}

Тогда просто используйте это так:

Foo& f = make_foo(condition, myString, myInteger);

Обратите внимание, что это еще один взлом VC, так как в соответствии со стандартным временным не может быть назначена ссылка на изменяемый объект, то есть он должен быть изменен const Foo&что было бы весьма ограничивающим здесь.

Не сказать, что вы должны справиться с этим, но это возможно.

2

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

У вас есть недостаток в вашем дизайне. ‘Foo’ делегирует тип elision пользователю класса. Посмотрите на boost :: any (http://www.boost.org/doc/libs/1_55_0/doc/html/any.html)

1

Я думаю, что лучшим вариантом, отвечающим вашим требованиям (не динамически размещаемым, не копируемым, до C ++ 11), будет использование placement new,

Увидеть Вот для краткого примера.

boost::optional также может быть приемлемым вариантом для вас (в основном он делает это для вас внутренне, а также отслеживает, был ли объект инициализирован). Конструкция на месте немного грязная, хотя для optional имо.

1

Держите это маленьким и простым, я бы сказал. Если это очень локальная проблема, почему бы просто не повторить звонок?

if (condition)
{
Foo f(myString);
f.doSomethingComplicated();
}
else
{
Foo f(myInt);
f.doSomethingComplicated();
}

Если это не представляется возможным, заверните Foo указатель (или умный указатель) в новом классе.

class FooWrapper // copyable
{
private:
boost::shared_ptr<Foo> m_foo;
public:
FooWrapper(std::string const &s) : m_foo(new Foo(s)) {}
FooWrapper(int i) : m_foo(new Foo(i)) {}
void doSomethingComplicated() { m_foo->doSomethingComplicated(); }
};

FooWrapper foo = condition ? FooWrapper(myString) : FooWrapper(myInt);
foo.doSomethingComplicated();
0

Используя C ++ 11 вы можете сделать:

Foo&& f = condition ? Foo{myInteger} : Foo{myString};
f.doSomethingComplicated();

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

0

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

const Foo &f(condition ? Foo(myString) : Foo(myInteger));

f.doSomethingComplicated(...);
0
По вопросам рекламы [email protected]