實戰c++中的智慧指標unique_ptr系列-- unique_ptr與lambda的錯誤結合(尤其是捕獲lambda中的unique_ptr)
阿新 • • 發佈:2019-02-15
lambda表示式是C++11新引入的東西,給我們帶來了很多的方便,使得程式碼簡潔明瞭。
但是當我們把unique_ptr和lambda表示式結合的時候,錯誤就往往會出現,而且是致命的。
直接看看下面的程式碼:
#include "stdafx.h"
#include <memory>
#include <vector>
#include <algorithm>
class Message {
public:
Message() {}
};
int main(int argc, char* argv[])
{
std::vector <std::unique_ptr<Message>>messages;
for (int i = 0; i < 1000; i++) {
std::unique_ptr<Message> testMess;
messages.push_back(std::move(testMess));
}
std::for_each(messages.begin(), messages.end(),
[](std::unique_ptr<Message> testMess) {
// do something stupid
});
return 0;
}
但是不幸的是,這段程式碼編譯就會產生錯誤,但也算是幸運的:
d:\program files (x86)\microsoft visual studio 14.0\vc\include\algorithm(24): error C2280: “std::unique_ptr<Message,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)”: 嘗試引用已刪除的函式
簡單分析
我們在lambda表示式中使用的是按值傳遞;
按值傳遞就會產生副本,就會產生一個unique_ptr的copy;
但是我們知道的,這顯然是錯誤。
解決方法很簡單,就是按引用傳遞替代按指標傳遞:
#include <iostream>
#include <memory>
#include <vector>
#include <algorithm>
class Message {
public:
Message() {}
};
int main(int argc, char* argv[])
{
std::vector<std::unique_ptr<Message>>messages;
for (int i = 0; i < 1000; i++) {
std::unique_ptr<Message> testMess;
messages.push_back(std::move(testMess));
}
std::for_each(messages.begin(), messages.end(),
[](std::unique_ptr<Message> &testMess) {
// do something stupid
});
return 0;
}
上面的內容只是算個開胃菜,如果我們想在lambda表示式中捕獲unique_ptr,又會如何呢?
十分優雅的寫下如下程式碼:
#include<iostream>
#include<memory>
#include<string>
int main()
{
auto str = std::make_unique<std::string>("my string");
auto lambda = [capturedStr = std::move(str)]{
std::cout << *capturedStr.get() << std::endl;
};
lambda();
return 0;
}
一切很完美,編譯 執行 輸出正確。
接下來,再幹點事兒:
#include<iostream>
#include<memory>
#include<string>
int main()
{
auto str = std::make_unique<std::string>("my string");
auto lambda = [capturedStr = std::move(str)] {
std::cout << *capturedStr.get() << std::endl;
auto str2 = std::move(capturedStr);
std::cout << *str2 << std::endl;
};
lambda();
return 0;
}
恭喜你,編譯錯誤。
原因為何呢?為什麼 auto str2 = std::move(capturedStr);會錯誤呢?
這就是lambda表示式的知識了:
lambda表示式預設是const的,我們當然不能std::move一個const物件。
解決方法也很簡單,就是加入關鍵字mutable
程式如下,執行完好:
#include<iostream>
#include<memory>
#include<string>
int main()
{
auto str = std::make_unique<std::string>("my string");
auto lambda = [capturedStr = std::move(str)] ()mutable {
std::cout << *capturedStr.get() << std::endl;
auto str2 = std::move(capturedStr);
std::cout << *str2 << std::endl;
};
lambda();
return 0;
}