Тест интеллектуального указателя в цикле while: используйте оператор запятой?

Я недавно видел код, подобный этому:

// 3rd Party API: (paraphrased)
void APIResetIterator(int ID); // reset iterator for call to next()
Mogrifier* APINext(int ID); // User must delete pointer returned

...

typedef std::unique_ptr<Mogrifier> MogPtr;

...

const it listID = 42;
APIResetIterator(listID);
MogPtr elem;
while (elem.reset(APINext(listID)), elem) {
// use elem
}

Это хорошая идея? Это работает?


Я добавлю соответствующий цикл для легкой ссылки:

for (MogPtr elem(APINext(listID)); elem; elem.reset(APINext(listID));) {
// use elem
}

… не кажется мне действительно оптимальным …

0

Решение

Он должен работать. Немного хитро и не слишком очевидно, но в остальном делает работу. Возможно, использование цикла for сделает код более понятным.

1

Другие решения

Это работает? Ну, должен, синтаксис действителен: указатель сбрасывается на значение, возвращаемое APINext(), то это проверено на NULL в while состояние.

Это хорошая идея? Это дело вкуса, но многим людям (включая меня) такой код не нравится. Это может быть законно и эффективно, но не так ясно, требуется время, чтобы понять. Для меня код читабельность очень важно, и этот конкретный код не является хорошим примером читабельности.

1

Как сказано в других ответах: это технически нормально и работает как задумано. Но то, что вы спрашиваете, на самом деле показывает, что это не очень хорошая идея, потому что ей не хватает читабельности.

Это своего рода замаскированный цикл for, похожий на этот:

int i = -1;
while (++i, i<10) { something(i); }

Другими словами, вы можете просто сделать это более понятным, фактически используя цикл for:

for (MogPtr elem{APINext(listID)}; elem != nullptr; elem.reset(APINext(listID)))
{
// use elem
}

Единственное, что вы должны ввести APINext дважды (шок!), что, вероятно, является причиной того, что кто-то написал так, как сейчас.

Извлеченный урок: удобочитаемость перевешивает лень.

Редактировать: IMO это на самом деле хорошая вещь, чтобы напечатать APINext(listID) дважды, потому что ясно, что в первый раз это инициализация, а в другой раз — переназначение.

Edit2: Iterator/Next() комбинация может выглядеть немного необычно в C ++, поскольку итераторы стандартной библиотеки C ++ работают с перегрузкой операторов. В Java и других языках без перегрузки операторов это нормальный способ работы. Если хотите, вы можете написать простой итератор в стиле C ++, заключающий вызовы API:

class MogrifierIterator {
MogPtr ptr_;
int listID_
public:
MogrifierIterator() : ptr_(nullptr) {} //end-Iterator
explicit MogrifierIterator(int listID) : ptr(nullptr), listID_(listID)  {
APIResetIterator(listID_);
ptr_.reset(APINext(listID_));
}

Mogrifier& operator*() { return *ptr_; }
Mogrifier* operator->() { return ptr_.get(); }
MogrifierIterator& operator++() { ptr_.reset(APINext(listID_)); return *this; }

bool operator==(MogrifierIterator const& other)
{ return (ptr_==other.ptr_) && (ptr_ == nullptr || listID_ == other.listID_); }
};

//...
for (MogrifierIterator it(listID); it != Mogrifieriterator(); ++it)
{
it->mogrify();
}

Это не полный, я не проверял это, и это может содержать ошибки, но Вы получаете суть 🙂

1

Это определенно работает, но лучше ли это, чем цикл for — это еще одна дискуссия. В этом случае он избегает двойного кода, потому что получение первого элемента аналогично получению следующего элемента (за исключением APIResetIterator(listID);). Так что это будет о идиоматическом кодировании (для цикла) или об избежании двойного кода (получение первого элемента и получение следующего элемента).

Мой совет — использовать итераторы в цикле for, но в этом случае это может быть не очень хорошо.

0

Возможно мы должны написать это как:

template<class SPT, typename P>
SPT& reset(SPT& smartPtr, P ptr) {
smartPtr.reset(ptr);
return smartPtr;
}

for (MogPtr elem; reset(elem, APINext(listID));) {
// use elem
}
0
По вопросам рекламы [email protected]