為什麼這段代碼在vs2017裡面emplace_back要求複製構造函數?

// 頭文件省略
class Foo
{
public:
Foo() = default;
Foo(const Foo) = delete;
};

int main()
{
vector& svec;
svec.emplace_back();

return 0;
}

錯誤 C2280 「Foo::Foo(const Foo )」: 嘗試引用已刪除的函數 test c:program files (x86)microsoft visual studio2017enterprisevc oolsmsvc14.10.25017includexmemory0 840

按道理 emplace_back 應該不要求複製構造函數吧?

而在其他編譯器比如 gcc 和 clang 在 -std=c++11 上面都正常通過?


看來代碼改了。我來指出真正的原因。因為複製構造函數要自動生成的話,前提是你沒有自己寫一個。你現在=delete了,你也屬於自己寫,所以那個Foo(Foo)就沒有自動生成了。你得自己寫一個Foo(Foo),代碼就能通過編譯了。

之所以clang++沒報錯,我猜是因為你的emplace_back沒有參數,所以直接用了默認構造函數。VC++的實現是無論如何都會去move一次的。如果你強行在clang裡面move一個Foo,你會發現你也編譯不了。


emplace_back不要求copy constructable,但是vector自己要求。當預留的空間不夠時,vector會擴展空間並且copy之前的數據,所以需要copy constructor。因為你自己delete了那個copy constructor,所以就編譯不通過了。

C++11中vector也可以利用move constructor。本來編譯器會自動生成move constructor,但是因為你主動聲明(聲明delete)了copy constructor,根據c++11的規則,編譯器不會再自動生成move。解決辦法之一是加上一句Foo(Foo) = defau< 來顯式告訴編譯器自動生成move,這樣vector就會自動用move而不是copy了。

但是注意如果你自己定義這個move constructor,則它必須是noexcept的時候vector才會用它代替copy。

推薦新用C++11的看看Effective Modern C++。

參考代碼:

#include &
#include &
#include &

class MyClass
{
public:
typedef enum
{
e1 = 1,
e2 = 2,
e3 = 3,
} Type;
private:
Type _type;
public:
MyClass(Type type): _type(type) { std::cout &<&< "create " &<&< type &<&< " "; }; MyClass(const MyClass other) { std::cout &<&< "copy " &<&< other._type &<&< " "; }; //MyClass(MyClass other) { std::cout &<&< "move " &<&< other._type &<&< " "; }; // Option 1: not OK, will call copy instead MyClass(MyClass other) noexcept { std::cout &<&< "move " &<&< other._type &<&< " "; }; // Option 2: OK //MyClass(MyClass other) = defau< // Option 3: OK }; int main() { std::vector& list;
list.reserve(2);
list.emplace_back(MyClass::e1);
list.emplace_back(MyClass::e2);
list.emplace_back(MyClass::e3);
}


看來輪子哥已經指出了真正的原因。那我就...來指出真正的原因背後的原因。

因為標準(n4659)規定了

6 If the class de?nition does not explicitly declare a copy constructor, a non-explicit one is declared implicitly. If the class de?nition declares a move constructor or move assignment operator, the implicitly declared copy constructor is de?ned as deleted; otherwise, it is de?ned as defaulted (11.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.

8 If the de?nition of a class X does not explicitly declare a move constructor, a non-explicit one will be implicitly declared as defaulted if and only if
—(8.1) X does not have a user-declared copy constructor,
—(8.2) X does not have a user-declared copy assignment operator,
—(8.3) X does not have a user-declared move assignment operator, and
—(8.4) X does not have a user-declared destructor.


推薦閱讀:

camtasia studio錄完的文件為什麼導不進pr?
C++斷點問題求解?
想在ASP.net中整合Bootstrap的LESS源碼,該怎麼做?
開機總是有一個彈窗 http://www.msftconnecttest.com/redirect ?

TAG:C | MicrosoftVisualStudio | GCC | Clang |