[英]Why is a copy constructor being called when passing an atomic into a variadic argument?
[英]Why is the copy constructor called when I am not passing an instance of the class?
我有一個具有構造函數的Server類:
Server::Server(int port) {
// initialize some class variables
port_ = port;
//...
}
我嘗試創建此類的實例,如下所示:
int main(int argc, char** argv) {
int port = 3000;
Server server = Server(port);
}
我得到這個編譯錯誤:
server_main.cpp:32:32: error: use of deleted function ‘Server::Server(const Server&)’
Server server = Server(port);
^
現在,我了解了為什么隱式刪除了副本構造函數,但是為什么要調用它呢?
如果將復制構造函數添加到類中,該錯誤就會消失。 還有其他方法可以避免這種情況嗎?
Server server = Server(port);
是復制初始化 ; 您正在從臨時Server
初始化server
。
復制省略可能會發生,但要等到C ++ 17才能保證。 甚至copy / move-constructor可能不會被調用,但仍然必須存在並且可以訪問(好像根本沒有優化發生),否則程序格式錯誤。
您可以將其更改為直接初始化 ,這將直接調用Server::Server(int)
:
Server server(port);
或直接列表初始化 (自C ++ 11起):
Server server{port};
編輯
從C ++ 17開始,在這種情況下可以保證復制省略 。
在以下情況下,即使復制/移動構造函數和析構函數具有明顯的副作用,也要求編譯器忽略類對象的復制和移動構造函數:
在初始化中,如果初始化器表達式是prvalue,並且源類型的cv不合格版本與目標類是同一類,則初始化器表達式用於初始化目標對象:
T x = T(T(T())); // only one call to default constructor of T, to initialize x
因此,您的代碼將與C ++ 17配合使用; 為了保證復制省略,不需要訪問復制/移動構造函數。
從非常令人討厭的書呆子角度來看,許多當前提供的答案(如果不是全部的話)會產生誤導。
在C ++中,左側和右側具有相同類型的復制初始化以特殊方式處理:它立即被解釋為等效的直接初始化。
從[dcl.init] / 16:
—如果目標類型是(可能是cv限定的)類類型:
—如果初始化是直接初始化, 或者是復制初始化,其中源類型的cv不合格版本與destination的類相同,或者是其派生類 ,則考慮構造函數...
這意味着您的副本初始化
Server server = Server(port);
實際上被當作直接初始化
Server server(Server(port));
並根據直接初始化的規則進行進一步處理。
直接初始化的規則說,重載解析用於選擇一個構造函數,在這種情況下選擇的構造函數是copy-constructor(在您的情況下被刪除,因此是錯誤)。
因此,最終結果是相同的-需要復制構造函數。 但是,要求它的標准邏輯的“分支”不是負責復制初始化的,而是負責直接初始化的。
在這種情況下,差異純粹是概念上的。 但是,在C ++ 98時代,這種模糊的區別在[現在已被遺忘的] std::auto_ptr
指針(re: auto_ptr_ref
及其工作方式)的功能中發揮了重要作用。 實際上,這通常被視為Move Constructor模式的早期慣用實現( https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Move_Constructor )。
一個簡單的示例說明特殊處理可能如下所示
struct A
{
A() {}
A(A&) {}
A(int) {}
operator int() const { return 42; }
};
struct B
{
B();
operator int() const { return 42; }
};
int main()
{
A a1 = A(); // OK
A a2 = B(); // Error
}
請注意,即使右側的兩個類都提供了用戶定義的int
轉換,也只有第一個初始化才編譯並使用A::A(int)
構造函數。 第二個失敗。
第二次初始化按照通常的復制初始化規則進行。 為了成功,它需要兩次用戶定義的轉換( B -> int
和int -> A
),這些轉換不能隱式完成。
根據直接初始化規則處理第一次初始化,從而有效地使int -> A
轉換成為顯式。 現在,此初始化僅需要一個隱式用戶定義的轉換( A -> int
),這很好。
因為您復制初始化server
對象。
定義
Server server = Server(port);
相當於
Server server(Server(port));
您可能想通過以下方式顯式使用構造函數
Server server(port);
復制初始化 , =
語法如Server server = Server{port};
,要求復制構造函數或move構造函數存在且可訪問。
由於您的副本構造函數不存在,請嘗試提供move構造函數。
如果不能,那么唯一的辦法就是使用直接初始化語法,例如Server server{port};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.