Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a better way in C++11 to construct classes on the stack

If I have two classes D1 and D2 that both derive from class Base, and I want to construct a particular one based on say, a boolean variable, there are various well known techniques, eg use a factory, or use smart pointers.

For example,

    std::unique_ptr<Base> b;
    if (flag)
    {
            b.reset(new D1());
    }
    else
    {
            b.reset(new D2());
    }

But this uses the heap for allocation, which is normally fine but I can think of times where it would be good to avoid the performance hit of a memory allocation.

I tried:

Base b = flag ? D1() : D2();      // doesn’t compile

Base& b = flag ? D1() : D2();     // doesn’t compile

Base&& b = flag ? D1() : D2();    // doesn’t compile

Base&& b = flag ? std::move(D1()) : std::move(D2());   // doesn’t compile

My intention is that D1 or D2 whichever is chosen is constructed on the stack, and its lifetime ends when b goes out of scope. Intuitively, I feel there should be a way to do it.

I played with lambda functions and found that this works:

Base&& b = [j]()->Base&&{
                 switch (j)
                 {
                 case 0:
                       return std::move(D1());
                 default:
                       return std::move(D2());
                 }
          }();

Why it doesn’t suffer from the same issues as the others that do not compile I do not know. Further, it would only be suitable for classes that are inexpensive to copy, because despite my explicit request to use move, it does I think still call a copy constructor. But if I take away the std::move, I get a warning!

I feel this is closer to what i think should be possible but it still has some issues:

  • the lambda syntax is not friendly to old-timers who havent yet embraced the new features of the language ( myself included)
  • the copy constructor call as mentioned

Is there a better way of doing this?

like image 259
user3564395 Avatar asked Dec 20 '22 15:12

user3564395


1 Answers

If you know all the types, you can use a Boost.Variant, as in:

class Manager
{   
    using variant_type = boost::variant<Derived1, Derived2>;

    struct NameVisitor : boost::static_visitor<const char*>
    {   
        template<typename T>
        result_type operator()(T& t) const { return t.name(); }
    };  

public:
    template<typename T>
    explicit Manager(T t) : v_(std::move(t)) {}

    template<typename T>
    Manager& operator=(T t)
    { v_ = std::move(t); return *this; }

    const char* name()
    { return boost::apply_visitor(NameVisitor(), v_); }

private:
        variant_type v_; 

};  

Note: by using variant, you no longer need a base class or virtual functions.

like image 142
Nevin Avatar answered Apr 30 '23 17:04

Nevin