问题
I recently saw code like this:
// 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
}
Is this a good idea? Does it work?
I'll add the corresponding for loop for easy reference:
for (MogPtr elem(APINext(listID)); elem; elem.reset(APINext(listID));) {
// use elem
}
... doesn't strike me as really optimal either ...
回答1:
As said in the other answers: This is technically ok and works as intended. But that you are questioning it actually shows that it's not a good idea, because it lacks readability.
This is sort of a disguised for-loop, similar to this one:
int i = -1;
while (++i, i<10) { something(i); }
In other words, you can just make it clearer by actually using a for loop:
for (MogPtr elem{APINext(listID)}; elem != nullptr; elem.reset(APINext(listID)))
{
// use elem
}
Only thing is that you have to type APINext
twice (shock!) which is probably the reason why someone had written it the way it is now.
Lesson learned: Readability outweighs laziness.
Edit: imo it's actually a good thing to type APINext(listID)
twice, because it makes clear that the first time it's actually an initialization, the other times, it's reassignment.
Edit2: The Iterator/Next()
combination may look a bit unusual in C++, since C++ standard library iterators work with operator overloading. In Java and other languages without operator overloading, this is the normal way to do things. If you want, you can write a simple C++ style forward iterator wrapping the API calls:
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();
}
It's not complete, I have not tested it and it may contain errors, but you get the gist :)
回答2:
It should work. A bit tricky and not too obvious but otherwise does the job. Maybe using a for cycle would make the code clearer.
回答3:
Does it work? Well, it should, the syntax is valid: the pointer is reset to the value returned by APINext()
, then it's tested for NULL
in the while
condition.
Is this a good idea? It's a matter of taste, but lots of people (including me) don't like that kind of code. It might be legal and working, but it is not that clear, takes a time to understand. For me, code readability very important and this particular code isn't good example of readability.
回答4:
It definitively works, but whether it is better than a for loop is another debate. In this case, it avoids double code because getting the first element is the same as getting the next element (except for the APIResetIterator(listID);
). So this would be about idiomatic coding (for loop) or avoiding double code (getting first element and getting next element).
My advice would be to use iterators in a for loop, but this might be not that good in this case.
回答5:
Maybe we should write it as:
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
}
来源:https://stackoverflow.com/questions/15022085/smart-pointer-test-in-a-while-loop-use-the-comma-operator