С++: Вызов различных конструкторов в зависимости от пользовательского ввода?

Есть условный код который конструирует объект в одном случае строкой, в другом - числом (суть в том, что разными типами и соответственно вызывает разные конструкторы):
Foo foo;
if (someFlag)
  foo = Foo("SomeString");
else 
  foo = Foo(42);

Если я не ошибаюсь, то данный код вызовет как минимум один лишний конструктор по умолчанию и один оператор присваивания. Если объявление foo внести в сам if-else блок, то проблема с областью видимости.

Вопрос как сделать такой условный вызов конструктор правильно и без оверхеда?

На ум приходит только вариант с указателем и foo = new Foo внутри if-else, но это надо потом не забыть delete(unique_ptr слишком многословен и хотелось бы вообще избежать указателей).

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

UPD2: а может есть вариант расширить область видимости за пределы блока if-else в случае если там объявлять переменную?

UPD3: Всем спасибо за решения, было интересно почитать.
Самыми адекватными вариантами на данный момент выглядят copy-elision c функцией возвращающей объект в зависимости от флага и указатель. Хотя на мой взгляд оба требуют лишних телодвижений со стороны программиста.
  • Вопрос задан
  • 379 просмотров
Решения вопроса 2
@MiiNiPaa
Вариант с функцией-генератором:

inline Foo make_foo(bool someFlag)
{
    if (someflag)
        return  Foo("SomeString");
    else 
        return Foo(42);
}
//...
Foo = make_foo(someflag)
Ответ написан
Комментировать
@MarkusD Куратор тега C++
все время мелю чепуху :)
Итак, ссылки потеме:
en.cppreference.com/w/cpp/language/copy_elision - буквально, используя стандартные языковые средства мы буквально избегаем конструктора копирования и оператора присвоения. Заострять внимание на RVO и NRVO.
en.cppreference.com/w/cpp/utility/move - семантика перемещения. Поддержи ее в своем классе для конструктора и оператора присвоения.

Для перемещения даже твой сферически условный код не надо будет править.
Хотя... тут в целом, даже при использовании copy elision этот код не надо править.

Да и вообще. Оптимизаторы ведущих (CL, ICC, GCC, Clang) компиляторов уже достаточно умные чтобы избавить тебя от головной боли по этому вопросу именно в этом конкретном месте. В результате программа сконструирует объект правильным конструктором в правильном месте, а конструктор по умолчанию и оператор присвоения - выкинет на мороз.
Ответ написан
Пригласить эксперта
Ответы на вопрос 2
@Mercury13
Программист на «си с крестами» и не только
1. Свой код — сделать функцию init
Foo foo;
if (someFlag)
  foo.init("SomeString");
  else foo.init(42);

2. Чужой код — сделать обёртку, которая в init будет использовать placement new, а в деструкторе — явно вызывать деструктор объекта.
class FooWrap {
public:
  FooWrap() : hasFoo(false) {}
  void init(int x) { new (fooPlace) Foo(x); hasFoo = true; }
  Foo& operator * () { return *reinterpret_cast<Foo*>(fooPlace); }
  Foo* operator -> () { return reinterpret_cast<Foo*>(fooPlace); }
  ~FooWrap() { if (hasFoo) (*this)->~Foo(); }
  // Да, и конструктор копирования и op= не забыть — оставлю как упражнение.
private:
  char fooPlace[sizeof(Foo)];
  bool hasFoo;
}

FooWrap foo;
if (someFlag)
  foo.init("SomeString");
  else foo.init(42);
foo->doFoo();

Для большей надёжности стоит указать, что FooWrap выравнивать как Foo — это делается через C++11 или расширениями компилятора.
Ответ написан
Rsa97
@Rsa97
Для правильного вопроса надо знать половину ответа
Использовать указатель
Foo *foo;
if (someFlag)
  foo = new Foo("SomeString");
else 
  foo = new Foo(42);
...
delete foo;
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы