2

Consider a class with a deleted default constructor (my actual class is much more complicated and the default constructor is deleted implicitly due to one of its members having a deleted default constructor). I would like to instantiate my class with one of its constructors based on the value of some input and then do "a bunch of things". The following code shows two ways that this can't be done for the reasons stated in the code comments, but gives an idea of what I'm trying to achieve.

#include <vector>
class A {
    bool m_with_v;
    int m_i;
    std::vector<double> m_v;
public:
    A() = delete;
    A(int i) : m_i(i), m_with_v(false) {}
    A(int i, std::vector<double> v) : m_i(i), m_v(v), m_with_v(true) {}
    auto do_stuff() { /* do stuff depending on m_with_v */ };
};

int main(int argc, char* argv[])
{
    bool yes_no;
    /* Obtain a value for yes_no from argv */

    A a; // the default constructor of "A" cannot be referenced -- it is a deleted function
    if (yes_no)
        a = A(1);
    else {
        std::vector<double> v{ 1,2,3 };
        a = A(1, v);
    }
    a.do_stuff();
    // do a bunch more things

    if (yes_no)
        A b(1);
    else {
        std::vector<double> v{ 1,2,3 };
        A b(1, v);
    }
    b.do_stuff(); // identifier "b" is undefined, i.e. it's gone out of scope
    // do a bunch more things
}

One obvious way to do this would be to move the "bunch of things" inside each part of the if block, but that would result in a lot of duplicate code, which I want to avoid.

Another obvious way would be to create a function to do the "bunch of things" and call that in each part of the if block, but this requires a certain amount of refactoring and the function call could become fairly ugly if there are a large number of variables that need to be passed to it.

So, my question is: Is there some way to conditionally instantiate my class and have it available in the surrounding scope?

Brett Ryland
  • 1,045
  • 1
  • 9
  • 17

4 Answers4

4

You can instantiate it using the conditional operator:

A a = yes_no ? A(1) : A(1, {1,2,3});
a.do_stuff();

and so on.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
2
auto a = [&]{
    if (yes_no) {
        return A(1);
    } else {
        std::vector<double> v{ 1,2,3 };
        return A(1, v);
    }
}();
a.do_stuff();
yuri kilochek
  • 12,709
  • 2
  • 32
  • 59
  • While juanchopanza's solution answers my question more simply, I ended up using this solution as the vector `v`, which is non-trivially generated and only needs to be generated for that choice of the constructor, doesn't end up polluting the surrounding scope. – Brett Ryland Feb 16 '17 at 23:34
0

You can make a free function (maybe static in A::, R Sahu is in favor of a free function) return an appropriate a.

A a = buildA(yes_no);

buildA would be

A buildA(bool yes_no){
   if (yes_no) 
       return A(1);

and so on.

Captain Giraffe
  • 14,407
  • 6
  • 39
  • 67
  • 1
    There's no need to pollute `A` with the additional logic. It's better to have it as non-member function. – R Sahu Feb 16 '17 at 23:00
0

You could consider using a unique_ptr<A> here. This way you could postpone actual object creation and make a proper instance inside the conditional blocks you require. For example:

std::unique_ptr<A> ptrA;
if (yes_no)
{
    ptrA.reset(new A(1));
}
else
{
    ptrA.reset(new A(1,{1,2,3}));
}

Depending on the actual version of c++ you are using you might want to be using std::make_unique instead of the reset method (which is available since C++14). In this case you would put ptrA = std::make_unique<A>(1);.

Another option would be to use a ternary operator A a = (yes_no) ? A(1) : A(1, {1, 2, 3}).

Or as others suggested, extract the object creation logic to a separate function where you can return objects from the if blocks depending on the fulfillment of yes_no conditions.

Dusteh
  • 1,496
  • 16
  • 21