delete又未完全delete
假設(shè)我們要定義一個(gè)這樣的類:只支持拷貝操作不支持移動(dòng)操作。
#include <iostream>
class Test {
public:
Test() {
std::cout << "constructor\n";
}
Test(const Test& test) {
std::cout << "copy constructor\n";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operator\n";
return *this;
};
Test(Test&& test) = delete;
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return test;
}
int main() {
Test test{ getTest() };
return 0;
}
輸出
main.cpp: In function 'Test getTest()':
main.cpp:16:12: error: use of deleted function 'Test::Test(Test&&)'
16 | return test;
| ^~~~
main.cpp:10:5: note: declared here
10 | Test(Test&& test) = delete;
| ^~~~
main.cpp:16:12: note: use '-fdiagnostics-all-candidates' to display considered candidates
16 | return test;
| ^~~~
從編譯錯(cuò)誤信息可以看出,被顯式delete的Test(Test&& test)參與到了重載決議,且被認(rèn)為比拷貝構(gòu)造版本更好,但移動(dòng)構(gòu)造版本被指定了刪除不能被使用,所以編譯器報(bào)錯(cuò)。
指定了刪除,卻未完全刪除,參與到了重載決議,有點(diǎn)反直覺。
奇怪的知識(shí)增加了
刪除的移動(dòng)構(gòu)造函數(shù)仍然是一個(gè)已聲明的函數(shù),有資格進(jìn)行重載解析。getTest函數(shù)按值返回將優(yōu)先選擇刪除的移動(dòng)構(gòu)造函數(shù)而非未刪除的復(fù)制構(gòu)造函數(shù)。
#include <iostream>
class Test {
public:
Test() {
std::cout << "constructor\n";
}
Test(const Test& test) {
std::cout << "copy constructor\n";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operator\n";
return *this;
};
Test(Test&& test) = default;
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return test;
}
int main() {
Test test{ getTest() };
return 0;
}
輸出
constructor
#include <iostream>
class Test {
public:
Test() {
std::cout << "constructor\n";
}
Test(const Test& test) {
std::cout << "copy constructor\n";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operator\n";
return *this;
};
Test(Test&& test) {
std::cout << "move constructor\n";
}
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return test;
}
int main() {
Test test{ getTest() };
return 0;
}
輸出
constructor
將Test(Test&& test) = delete修改為Test(Test&& test) = default或者顯式定義雖可以修復(fù)此問(wèn)題,然而,之前的本意就是將移動(dòng)構(gòu)造刪除,讓其使用拷貝構(gòu)造啊,如此修改違背了本意。
更進(jìn)一步的觀察,竟然未輸出move constructor!重載決議選擇了移動(dòng)構(gòu)造,但未執(zhí)行移動(dòng)構(gòu)造,有點(diǎn)反直覺,此問(wèn)題請(qǐng)參考尾返回值優(yōu)化。
C++11之前可以通過(guò)“只聲明不實(shí)現(xiàn)”的方式要?jiǎng)h除函數(shù),既然尾返回值優(yōu)化可以優(yōu)化掉拷貝構(gòu)造的調(diào)用,那我們可以保證編譯通過(guò)即可。
#include <iostream>
class Test {
public:
Test() {
std::cout << "constructor\n";
}
Test(const Test& test) {
std::cout << "copy constructor\n";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operator\n";
return *this;
};
Test(Test&& test);
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return test;
}
int main() {
Test test{ getTest() };
return 0;
}
輸出
constructor
從上述代碼表現(xiàn)來(lái)看,移動(dòng)構(gòu)造只參與了重載決議,未參與鏈接和運(yùn)行期運(yùn)行!
最后我們?cè)賴L試一個(gè)移動(dòng)構(gòu)造參與鏈接器鏈接的版本:
#include <iostream>
class Test {
public:
Test() {
std::cout << "constructor\n";
}
Test(const Test& test) {
std::cout << "copy constructor\n";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operator\n";
return *this;
};
Test(Test&& test);
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return std::move(test);
}
int main() {
Test test{ getTest() };
return 0;
}
輸出
/tmp/cc3TH4WE.o: In function `getTest()':
main.cpp:(.text+0x32): undefined reference to `Test::Test(Test&&)'
collect2: error: ld returned 1 exit status
重載決議選擇了移動(dòng)構(gòu)造,但是鏈接時(shí)未找到移動(dòng)構(gòu)造的實(shí)現(xiàn)!