while循环中的智能指针测试:使用逗号运算符

Smart pointer test in a while loop: use the comma operator?

本文关键字:运算符 测试 循环 智能 指针 while      更新时间:2023-10-16

我最近看到这样的代码:

// 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循环以便于参考:

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

我觉得也不是最理想的。。。

正如其他答案中所说:这在技术上是可以的,并且可以按预期工作。但你质疑它实际上表明这不是一个好主意,因为它缺乏可读性。

这有点像一个伪装的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();
}

它还不完整,我还没有测试过,它可能包含错误,但你会得到要点:)

它应该可以工作。这有点棘手,也不太明显,但在其他方面也能起到作用。也许使用for循环会使代码更清晰。

它能工作吗?应该,语法是有效的:指针重置为APINext()返回的值,然后在while条件下测试NULL

这是个好主意吗?这是一个品味问题,但很多人(包括我)不喜欢那种代码。这可能是合法和有效的,但并不那么清楚,需要一段时间才能理解。对我来说,代码的可读性非常重要,而这个特定的代码并不是可读性的好例子。

它确实有效,但它是否比for循环更好是另一个争论。在这种情况下,它避免了双重编码,因为获得第一个元素与获得下一个元素相同(除了APIResetIterator(listID);)。因此,这将涉及惯用编码(针对循环)或避免双重编码(获取第一个元素和下一个元素)。

我的建议是在for循环中使用迭代器,但在这种情况下可能不太好。

也许我们应该把它写成:

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
}