假設我有一個堆疊分配的大 blob 物件。我需要把它放在一個包裝物件中,但我想避免復制。我應該只std::move與移動建構式一起使用嗎?證明它有效的最簡單方法是什么?
struct Blob {
char blob[1024 * 1024]; // imagine something big here
};
template <typename T>
struct Foo {
Foo(T&& src) : data{src} {}
T data;
};
int main() {
Blob blob;
Foo foo{std::move(blob)}; // do not copy
// should not take twice the memory of a blob
}
uj5u.com熱心網友回復:
在這種情況下,Blob是普通的舊資料。編譯器可以自由地優化Blob blob不存在的變數。
它也可以無緣無故地免費制作一百萬份。標準不限制它。
有一種優化叫做“靜態單賦值”,將區域物件狀態表示為獨立的存在變數,它允許編譯器擺脫一堆無意義的副本或其他無關緊要的狀態變化。足夠復雜的代碼,或優化范圍之外的參考/指標泄漏,將其阻止。
因此,在實踐中,只是不要阻止編譯器優化Blob blob的存在。
話雖如此
Foo(T&& src) : data{src} {}
這應該讀
Foo(T&& src) : data{std::forward<T>(src)} {}
另外,你可以做一個
Foo<Blob&> foo(blob);
并Foo存盤對Blob資料的參考。Foo然而,這以令人討厭的方式改變了“價值語意” 。
請注意,您的隱式推導指南同樣瘋狂,因為如果您傳遞左值,您將獲得一個Foo包裝參考,但如果您傳遞一個右值,您將獲得一個Foo包裝值。
如果你想要一個硬保證,C 不提供。C 甚至不保證Blob blob;實際占用堆疊空間。
更進一步,你可以這樣做:
int main() {
Foo foo{[&]{
Blob blob;
return blob;
}()};
}
在這種情況下,Blob blob的存在被忽略到回傳值中,回傳值又被傳遞給foo,然后回傳值的生命周期在完整運算式的末尾結束。除了 lambda,您還可以使用main.
這使編譯器更容易確定單獨的Blob物件沒有意義,但數量不多。
如果您想讓Blob blob直接省略Foo data更有保證,請添加一個Foo帶Blob工廠的建構式:
template <typename T>
struct Foo {
template<class U>
requires std::is_same_v< std::decay_t<U>, T >
Foo(U&& src) : data{std::forward<U>(src)} {}
template<class F>
requires std::is_invocable_r_v< T, F&& >
Foo(F&& f) : data(std::forward<F>(f)()) {}
T data;
};
template<class T>
requires !std::is_invocable_v< T&& >
Foo(T&&)->Foo<std::decay_t<T>>;
template<class F>
requires std::is_invocable_v< F&& >
Foo(F&&)->Foo<std::decay_t<std::invoke_result_t<F&&>>>;
int main() {
Foo foo{[&]{
Blob blob;
return blob;
}};
}
這可能會走得太遠。
uj5u.com熱心網友回復:
任何一個:
- 在包裝器中保留對原始物件的參考并通過 this 使用它。
- 使用 NRVO 避免復制。例如
struct Blob {
char blob[1024 * 1024]; // imagine something big here
};
template <typename T>
struct Foo {
template<typename = std::enable_if_t<std::is_default_constructible<T>::value>>
Foo(): data(){}
T data;
};
Foo<Blob> ProcessBlob(){
Foo<Blob> value{};
Blob& blob = value.data;
// Use `blob` here
return value; // NRVO would eliminate copying
}
void ProcessIn2Steps(){
Foo<Blob> wrapped = ProcessBlob();
// Use `wrapped` here
}
此外,如果您真的想避免復制,請考慮洗掉以下的復制建構式Foo:
template <typename T>
struct Foo {
template<typename = std::enable_if_t<std::is_default_constructible<T>::value>>
Foo(): data(){}
Foo(const Foo&) = delete;
Foo(Foo&&) = delete;
Foo& operator=(const Foo&) = delete;
Foo& operator=(Foo&&) = delete;
T data;
};
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/325434.html
標籤:C
下一篇:在C 中洗掉動態陣列會導致錯誤
