c++emplacestdoptional

How std::optional library handles emplace operation?


I was trying to create objects for optional variables in-place. When using emplace function in std optional library for a class, the destructor of the class is called. Does that function do copy operation or move operation for that classes.

I called emplace function in three different ways and two of them calls the destructor. What is the difference between them?

Basic class:

class myClass{
private:
   int m_num;
public:
   myClass(int num) : m_num(num){}
   ~myClass(){
      std::cout << "myClass destructed\n";
   }
};

Main:

int main(){
   std::optional<myClass> a;
   std::optional<myClass> b;
   std::optional<myClass> c;

   std::cout << "emplacing a: \n";
   a.emplace(myClass{0});
   std::cout << "emplacing b: \n";
   b.emplace<myClass>(0);
   std::cout << "emplacing c: \n";
   c.emplace(0);
   std::cout << "done\n";
   
   while (true) {};    
}

output:

emplacing a: 
myClass destructed
emplacing b:
myClass destructed
emplacing c:
done

Solution

  • If I add print statements to all the compiler generated methods I get the following output:

    emplacing a: 
    Constructor: 0
    Move Constructor: 0
    myClass destructed
    
    
    emplacing b: 
    Constructor: 0
    Move Constructor: 0
    myClass destructed
    
    
    emplacing c: 
    Constructor: 0
    
    done
    myClass destructed
    myClass destructed
    myClass destructed
    

    Note: The following statement are based on having either implicit or explicit move constructors created. Which is what I expect the OP is expecting to have answered by the question. BUT one should note that the implicit move constructors for the OPs myClass have been disabled because the class has a user defined destructor and thus it would use the copy constructor rather than the move constructor.

    So you can see in the first two. You are creating an object of myClass which is then being moved into the optional. Then the object that was created is destroyed.

    While you third option c simply creates the object emplace into the optional.

    I removed the infinte final loop. So you can see that after main() exits all three objects remaining are then destroyed.

    So what is happening. Usually emplace is written something like this:

     template<typename... Args>
     void emplace(Args...&& args)
     {
          // In the first two cases the parameter is `myClass` so
          // simply forward the parameter which invokes the move
          // constructor.
          //
          // In the third case the parameter is an int.
          // So forward this to the constructor of myClass there
          // is a constructor that takes an int so use it.
          createThe_T_object_insidethisobject(std::forward<Args>(args)...);
     }
    

    So in your case:

    a.emplace(myClass{0});   // Args is myClass
                             // So here you have manually create the myClass
                             // object before even passing it.
                             // This is fine as the forward will then do a perfect forward of the parameter.
    

    b.emplace<myClass>(0);   // Here you are telling the compiler
                             // You are invoking the function using
                             // Args => myClass
                             // So the compiler has to create an R-Value using the values you passed.
                             // Fine. Your user defined constructor works.
                             // Then inside the function you use perfect forwarding.
    

     c.emplace(0);           // Here Args => int
                             // You forward the parameters to where
                             // the `myclass` object is created.
                             // there is a constructor that takes an
                             // integer so build in place.
    

    PS. just in case it is not obvious. You should be using technique c (as in all C++ questions "usually").