c++stdoptional

Correct way of inserting std::optional<T> into std::vector<T>


I have a class T, it looks like this:

class T {
  uint64_t id;
  std::string description;
  char status;
  uint64_t createdAt;
  uint64_t updatedAt;

T(uint64_t c_id, std::string_view c_description, char c_status, uint64_t c_createdAt, uint64_t c_updatedAt) :
    id(c_id),
    description(c_description),
    status(c_status),
    createdAt(c_createdAt),
    updatedAt(c_updatedAt) {}

T(T&& other) :
    id(other.id),
    description(std::move(other.description)),
    status(other.status),
    createdAt(other.createdAt),
    updatedAt(other.updatedAt) {}
  // member functions
};

At some point, I need to append an optional into a vector. What is the best way between these options (or are there other options)?

std::optional<T> opt = myFunction();
std::vector<T> tasks;
if(opt.has_value()) {
  tasks.push_back(*opt);
  tasks.push_back(opt.value());
  tasks.push_back(std::move(*opt));

  tasks.emplace_back(*opt);
  tasks.emplace_back(opt.value());
  tasks.emplace_back(std::move(*opt));
}

Solution

  • // copies optional content into vector, optional unchanged
    // undefined behavior if optional was originally empty
    tasks.push_back(*opt); 
    
    // copies optional content into vector, optional unchanged
    // throws std::bad_optional_access if optional is empty
    tasks.push_back(opt.value());
    
    // moves the optional content into vector, opt std::string member is now empty
    // undefined behavior if optional was originally empty
    tasks.push_back(std::move(*opt));
    
    // moves the optional content into vector, opt std::string member is now empty
    // throws std::bad_optional_access if optional is empty
    tasks.push_back(std::move(opt.value()));
    

    the cheapest (generating the least amount of assembly instructions) would be

    if (opt) // get rid of UB in operator*
    {
      tasks.push_back(std::move(*opt)); // move the contents of opt into tasks
    }
    // bonus: same syntax as a pointer!
    

    there is no difference between push_back and emplace_back in this context. emplace_back is only useful when the type of the argument is different than the type of the container and you want to have a conversion. push_back will do a conversion then a move, whereas emplace_back will just do a conversion. Don't abuse emplace_back as it does explicit conversions implicitly.