Как лучше организовать функцию
Есть такой код:
template<class __T>
class IdContainer
{
std::vector<__T> unorderedIds_;
public:
__T getNext() {
auto _it = unorderedIds_.begin();
if (_it != unorderedIds_.end()) {
__T _id = *_it;
unorderedIds_.erase(_it);
return _id;
}
throw std::runtime_error("---");
};
}
то бишь в случае если вектор пуст я выбрасываю исключение, а если там что-то есть возвращаю первый элемент. Возможно лучше сделать так:
bool getNext(__T& id) {
auto _it = unorderedIds_.begin();
if (_it != unorderedIds_.end()) {
id = *_it;
unorderedIds_.erase(_it);
return true;
}
return false;
};
Еще я вижу вариант с возвратом указателя:
__T* getNext() {
return _id;
or
return nullptr;
}
но это прям перебор, выделять какой-нибудь int в куче.
Может есть и другие варианты которые я упускаю. Подскажите пожалуйста как лучше написать эту функцию. Ее идея в том что она должна выдать нам первый id иначе дать понять что id закончились.
Ответы (2 шт):
У вас есть целых 4 (вместе с optional) варианта решения данной проблемы, и все 4 варианта - хорошие.
Выбор конкретного варианта зависит от большого количества факторов:
От соглашений, принятых на проекте
От смысла выполняемой операции - обычно, исключение кидается, когда происходит ИСКЛЮЧИТЕЛЬНАЯ СИТУАЦИЯ, а программисты стараются не пускать обычную логику через исключения. Но это скорее вопрос соглашений, принятых на проекте.
Какие лучше выбрать соглашения для проекта?
Если вы учитесь, вы должны знать и понимать все варианты
Если вы начинаете новый проект в составе команды, вы будете принимать совместное решение.
Если решение должны принимать лично вы, ориентируйтесь на свой опыт. Если опыта нет - у вас учебный проект, см. пункт выше (вам следует изучить все варианты).
Если у вас нет возможности перейти на новый компилятор и стандарт C++17, то вполне можно закодировать аналог std::optional, это просто, вот для примера я попробовал закодировать подобный класс:
#include <utility>
#include <stdexcept>
template <typename T>
class Optional {
public:
Optional() {}
~Optional() { Destruct(); }
template <typename Q>
Optional(Q && v) { Construct(std::forward<Q>(v)); }
template <typename Q>
Optional & operator = (Q && v) { return Construct(std::forward<Q>(v)); }
Optional(Optional const & other) { Replace(other.has_val, other.val); }
Optional & operator = (Optional const & other) {
return Replace(other.has_val, other.val);
}
Optional(Optional && other) { Replace(other.has_val, std::move(other.val)); }
Optional & operator = (Optional && other) {
return Replace(other.has_val, std::move(other.val));
}
bool has_value() const { return has_val; }
operator bool() const { return has_val; }
T & value() {
if (!has_val)
throw std::runtime_error("Optional doesn't contain value!");
return val;
}
T const & value() const { return const_cast<Optional&>(*this).value(); }
T & operator * () { return val; }
T const & operator * () const { return val; }
T * operator -> () { return &val; }
T const * operator -> () const { return &val; }
void reset() { Destruct(); }
template <typename ... Args>
T & emplace(Args && ... args) {
Construct(std::forward<Args>(args)...);
return val;
}
private:
template <typename ... Args>
Optional & Replace(bool has, Args && ... args) {
return has ? Construct(std::forward<Args>(args)...) : Destruct();
}
template <typename ... Args>
Optional & Construct(Args && ... args) {
Destruct();
new (&val) T(std::forward<Args>(args)...);
has_val = true;
return *this;
}
Optional & Destruct() {
if (has_val) {
val.~T();
has_val = false;
}
return *this;
}
bool has_val = false;
struct Dummy {};
union {
Dummy dummy;
T val;
};
};
А так вообще я думаю что просто возвращать bool из функции вида bool getNext(__T& id) как вы предложили это вполне подходящий и простой вариант.
Варианты с возвращением указателя или исключением тоже рабочие, но если это высокопроизводительный участок кода, то лучше их не использовать, они сильно замедлят код. bool вариант при этом не испортит производительности.