最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Correct way of inserting std::optional<T> into std::vector<T> - Stack Overflow

programmeradmin3浏览0评论

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));
}

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));
}
Share Improve this question edited Mar 25 at 10:05 bramar2 asked Mar 25 at 9:13 bramar2bramar2 771 silver badge4 bronze badges 4
  • 4 "best" by which metric? Also, what should happen if the optional is empty? – Botje Commented Mar 25 at 9:16
  • 1 Are you sure the optional is non-empty? Also important but omitted: does T have a move constructor? You left out all member functions, including the most important one for this question. – MSalters Commented Mar 25 at 9:17
  • The snippet is just a recreation of my program, so I didn't add the check in there. I've also added the relevant constructors of T. – bramar2 Commented Mar 25 at 9:50
  • Offtopic: Be aware that T symbol name is usually used as template parameter. So when you use this as actual class name this is vary confusing for may developers. You should use Foo or Bar. – Marek R Commented Mar 25 at 11:38
Add a comment  | 

3 Answers 3

Reset to default 8
// 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.

In C++26, the simplest way to copy-insert the contents of opt is

std::optional<T> opt = myFunction();
std::vector<T> tasks;
tasks.append_range(opt);

And the simplest way to move-insert the contents of opt is

std::optional<T> opt = myFunction();
std::vector<T> tasks;
tasks.append_range(opt | std::views::as_rvalue);

Depending on your need, it might also be simpler to just hold a list of optionals

std::optional<T> opt = myFunction();
std::vector<std::optional<T>> maybeTasks;
maybeTasks.push_back(opt);

and worry later about wether they exist, or if all tasks are created and then handled separately:

std::vector<T> taskList;
for (auto it : maybeTasks) {
    if(it.has_value()) {
        taskList.push_back(it.value());
    }
}
发布评论

评论列表(0)

  1. 暂无评论