簡體   English   中英

可能失敗的 unique_ptr 移動的函數簽名?

[英]Function signature for potentially failed unique_ptr move?

假設我正在編寫一個接收unique_ptrenqueue()函數,但我只想在 enqueue 返回成功時聲明其所有權。 如果隊列已滿,我想保持unique_ptr不變(用戶可以稍后重試相同的項目)

bool enqueue(std::unique_ptr&& item){
  if(!vector.full()){
    vector.emplace(std::move(item));
    return true;
  }
  return false;
}
// usage
auto item_ptr = make_unique<>();
while(!enqueue(std::move(item_ptr))){
// item_ptr is not moved
}

我也可以定義函數來代替左值引用

bool enqueue(std::unique_ptr& item)
while(!enqueue(item_ptr)){
// item_ptr is not moved
}

我不確定選擇哪個,它們似乎都有點反模式,因為通常std::move表示刪除 unique_ptr(大多數情況下,我使用按值獲取unique_ptr的函數),也許有更好的解決方案?

帶有 RValue 引用的第一個版本接受一個臨時的。 因此,如果移動沒有發生,您在臨時std::unique_ptr有一個實例,它將很快被刪除。 這就是讓我不確定它是否是一個好的選擇(或會產生令人驚訝的效果)的原因。

至少,人們應該意識到這一點。

關於問題

如果你執行 while (!enqueue(make_unique<>())) 會發生什么...

我做了一個 MCVE(只是為了確定):

#include <memory>
#include <iostream>

struct Test {
  Test() { std::cout << "Test::Test()\n"; }
  ~Test() { std::cout << "Test::~Test()\n"; }
};

bool enqueue(std::unique_ptr<Test>&& item)
{
  return false;
}

int main()
{
  for (int i = 0; i < 3 && !enqueue(std::make_unique<Test>()); ++i);
}

輸出:

Test::Test()
Test::~Test()
Test::Test()
Test::~Test()
Test::Test()
Test::~Test()

在coliru上演示

如果旨在防止“濫用”並確保不為臨時調用enqueue()則可以刪除 RValue 版本。

你可以從你的函數中返回一個 unique_ptr 。 如果失敗返回原件。 如果成功,則返回一個空的 unique_ptr。

然后你可以這樣稱呼它:

#include <iostream>
#include <memory>
#include <vector>

int counter = 10;

template <typename T> std::unique_ptr<T> enqueue(std::unique_ptr<T> p) {
  if (--counter == 0)
    return std::unique_ptr<T>();
  return p;
}

int main() {
  auto item_ptr = std::make_unique<int>(8);
  while (item_ptr = enqueue(std::move(item_ptr))) {
    std::cout << "looping\n";
  }
  std::cout << "moved\n";
  return 0;
}

其實,我最好去試試這個。 是的,我的第一個想法有一個錯誤。 撕掉那個! 從while循環。 總是測試。 :-)

我喜歡的關於std::move的心態是,只有我表明我對被修改的對象沒有問題。 這不是關於導致刪除,而是關於允許潛在破壞性的事情發生。

你的第一個選擇對我來說似乎沒問題。 調用者必須通過用std::move標記他們的左值來明確授予權限,並且他們可以通過檢查函數的返回值來檢查是否確實發生了某些事情。 所以他們不會讓他們關心的對象突然消失。 IMO已經足夠

但就像另一個答案所說,這個版本還提供了傳遞臨時unique_ptr的選項。 如果在這種情況下無意義的工作讓您感到困擾,那么我們可能應該采用另一種方法。

有時我喜歡從 Google 樣式指南中獲取的一項准則是,[輸入/] 輸出參數由非擁有的原始指針傳遞。 這基本上將它們限制為左值,同時保留允許修改所需的明確性。 所以你可以這樣做:

bool enqueue(std::unique_ptr<T>* item){
  if(!vector.full()){
    vector.emplace(std::move(*item));
    return true;
  }
  return false;
}

// usage
auto item_ptr = make_unique<T>();
while(!enqueue(&item_ptr)){
// item_ptr is not modified
}

在這樣的風格指南下,地址運算符&的作用與std::move相同。 是我們告訴函數:“繼續,如果需要,修改它。”

這個解決方案對我來說似乎有點棘手,因為您正在傳遞unique_ptr ,但它可能會或可能不會被聲明,因此需要以某種方式處理這種行為:例如,在成功的情況下返回一個空的unique_ptr或錯誤/成功代碼,進行冗余或難以理解的檢查等。 我認為您可以做的是在 full() 方法或類似方法的幫助下檢查您的隊列是否有外部可用空間。 然后你的代碼看起來像這樣:

if(!full()) {
    enqueue(std::move(...));
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM