[英]Member initialization for non-copyable variable in C++17
對不可復制變量(例如std::atomic<int>
)執行成員初始化時,需要根據此處的答案使用direct-initialization
而不是copy-initialization
。 但是,當我在g++ 7.4.0
中打開-std=c++17
時,似乎后者也能正常工作。
#include <atomic>
class A {
std::atomic<int> a = 0; // copy-initialization
std::atomic<int> b{0}; // direct-initialization
};
$ g++ -c atomic.cc -std=c++11 // or c++14
atomic.cc:4:26: error: use of deleted function ‘std::atomic<int>::atomic(const std::atomic<int>&)’
std::atomic<int> a = 0; // copy-initialization
$ g++ -c atomic.cc -std=c++17
// no error
即使使用g++ 6.5.0
編譯,它也失敗了-std=c++17
。 這里哪一個是正確的?
自 C++17 以來行為發生了變化,這要求編譯器省略std::atomic<int> a = 0;
,即保證復制省略。
(強調我的)
在以下情況下,編譯器需要省略 class 對象的復制和移動構造,即使復制/移動構造函數和析構函數具有可觀察到的副作用。 對象直接構建到存儲中,否則它們將被復制/移動到。 復制/移動構造函數不需要存在或可訪問,因為語言規則確保不發生復制/移動操作,即使在概念上也是如此:
詳細地說, std::atomic<int> a = 0;
執行 復制初始化:
如果 T 是 class 類型,並且 other 的類型的 cv-unqualified 版本不是 T 或派生自 T,或者如果 T 是非類類型,但 other 的類型是 class 類型,用戶定義的轉換序列檢查可以從 other 的類型轉換為 T 的類型(或者如果 T 是 class 類型並且轉換 function 可用,則轉換為從 T 派生的類型)進行檢查,並通過重載決議選擇最佳的類型。 如果使用了轉換構造函數,則轉換結果是一個
prvalue temporary (until C++17)
prvalue expression (since C++17)
,然后用於直接初始化 object。
和
(強調我的)
如果 T 是 class 類型並且初始化程序是 prvalue 表達式,其 cv 非限定類型與 T 相同 class,則使用初始化程序表達式本身,而不是從它的臨時物化,來初始化目標 ZA8CFDE6331BD59EB2ACZFC6
這意味着a
直接從0
初始化,沒有要構造的臨時對象,也不再是要復制/移動的臨時對象。
在 C++17 之前,在概念上std::atomic<int> a = 0;
需要從0
構造一個臨時的std::atomic
,然后該臨時用於復制構造a
。
在 C++17 之前甚至允許復制省略,這被認為是一種優化:
(強調我的)
這是一種優化:即使它發生並且沒有調用復制/
move (since C++11)
構造函數,它仍然必須存在且可訪問(就好像根本沒有發生優化一樣),否則程序會出錯 -形成:
這就是為什么 gcc 在 pre-c++17 模式下觸發診斷std::atomic<int> a = 0;
.
(強調我的)
注意:上面的規則沒有指定優化:C++17 prvalues 和 temporaries 的核心語言規范與早期的 C++ 修訂版根本不同:不再有從. 描述 C++17 機制的另一種方式是“未實現的值傳遞”: prvalues 被返回並使用,而無需實現臨時的.
順便說一句:我想在g++ 6.5.0
中有一個錯誤-std=c++17
; 並且已在以后的版本中修復。
這里哪一個是正確的?
7.4.0 是正確的。 對於這種情況,可以省略副本,這就是為什么可以。 (盡管這需要c++17 )。
(有關詳細信息,請參閱https://en.cppreference.com/w/cpp/language/copy_initialization )
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.