ในยุคแห่งการรู้แจ้งของปี 2559 ด้วยมาตรฐานใหม่สองมาตรฐานภายใต้เข็มขัดของเราเนื่องจากคำถามนี้ถูกถามและคำถามใหม่ที่อยู่ใกล้ ๆ สิ่งสำคัญที่ต้องรู้คือคอมไพเลอร์ที่รองรับมาตรฐาน C ++ 17 จะรวบรวมโค้ดของคุณตามที่เป็นอยู่ .
การหักอาร์กิวเมนต์เทมเพลตสำหรับเทมเพลตคลาสใน C ++ 17
ที่นี่ (ได้รับความอนุเคราะห์จากการแก้ไขโดย Olzhas Zhumabek สำหรับคำตอบที่ยอมรับ) เป็นเอกสารที่มีรายละเอียดการเปลี่ยนแปลงที่เกี่ยวข้องกับมาตรฐาน
จัดการข้อกังวลจากคำตอบอื่น ๆ
คำตอบยอดนิยมในปัจจุบัน
คำตอบนี้ชี้ให้เห็นว่า "ตัวสร้างการคัดลอกและoperator=" ไม่ทราบถึงความเชี่ยวชาญพิเศษของเทมเพลตที่ถูกต้อง
นี่เป็นเรื่องไร้สาระเนื่องจากตัวสร้างสำเนามาตรฐานและoperator= มีอยู่สำหรับประเภทเทมเพลตที่รู้จักเท่านั้น:
template <typename T>
class MyClass {
MyClass(const MyClass&) =default;
... etc...
};
// usage example modified from the answer
MyClass m(string("blah blah blah"));
MyClass *pm; // WHAT IS THIS?
*pm = m;
ที่นี่ที่ผมตั้งข้อสังเกตในความคิดเห็นที่มีไม่มีเหตุผลสำหรับการMyClass *pmที่จะเป็นคำชี้แจงทางกฎหมายที่มีหรือไม่มีรูปแบบใหม่ของการอนุมาน: MyClass ไม่ได้เป็นประเภท (เป็นแม่แบบ) ดังนั้นจึงไม่ได้ทำให้ความรู้สึกที่จะประกาศตัวชี้ของ ประเภทMyClass. นี่เป็นวิธีหนึ่งที่เป็นไปได้ในการแก้ไขตัวอย่าง:
MyClass m(string("blah blah blah"));
decltype(m) *pm; // uses type inference!
*pm = m;
ที่นี่pmเป็นประเภทที่ถูกต้องอยู่แล้วดังนั้นการอนุมานจึงเป็นเรื่องเล็กน้อย ยิ่งไปกว่านั้นมันเป็นไปไม่ได้ที่จะผสมประเภทโดยบังเอิญเมื่อเรียกตัวสร้างสำเนา:
MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));
ที่นี่pmจะเป็นตัวชี้ไปยังสำเนาของmไฟล์. ที่นี่MyClassกำลังสร้างสำเนาจากm- ซึ่งเป็นประเภทMyClass<string>( ไม่ใช่ประเภทที่ไม่มีอยู่MyClass) ดังนั้นที่จุดที่pmพิมพ์ 's อนุมานได้มีเป็นข้อมูลที่เพียงพอที่จะรู้ว่าแม่แบบชนิดของmและดังนั้นจึงแม่แบบประเภทของการเป็นpmstring
ยิ่งไปกว่านั้นสิ่งต่อไปนี้จะทำให้เกิดข้อผิดพลาดในการคอมไพล์เสมอ :
MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;
เนื่องจากการประกาศของตัวสร้างการคัดลอกไม่มีเทมเพลต:
MyClass(const MyClass&);
ที่นี่ประเภทเทมเพลตของอาร์กิวเมนต์ copy-constructor ตรงกับ template-type ของคลาสโดยรวม กล่าวคือเมื่อMyClass<string>มีการสร้างอินสแตนซ์MyClass<string>::MyClass(const MyClass<string>&);จะถูกสร้างอินสแตนซ์กับมันและเมื่อMyClass<int>ถูกสร้างอินสแตนซ์MyClass<int>::MyClass(const MyClass<int>&);จะถูกสร้างอินสแตนซ์ เว้นแต่จะมีการระบุไว้อย่างชัดเจนหรือมีการประกาศตัวสร้างเทมเพลตไม่มีเหตุผลใดที่คอมไพลเลอร์จะสร้างอินสแตนซ์MyClass<int>::MyClass(const MyClass<string>&);ซึ่งเห็นได้ชัดว่าไม่เหมาะสม
คำตอบโดยCătălin Pitiș
Pitişให้ตัวอย่าง deducing Variable<int>และVariable<double>แล้วสหรัฐฯ:
ฉันมีชื่อประเภทเดียวกัน (ตัวแปร) ในรหัสสำหรับสองประเภทที่แตกต่างกัน (ตัวแปรและตัวแปร) จากมุมมองส่วนตัวของฉันมันมีผลต่อการอ่านโค้ดค่อนข้างมาก
ที่ระบุไว้ในตัวอย่างก่อนหน้านี้Variableตัวเองเป็นไม่ได้ชื่อประเภทแม้ว่าคุณลักษณะใหม่ที่ทำให้มันมีลักษณะเหมือนหนึ่ง syntactically
จากนั้น Pitiș ก็ถามว่าจะเกิดอะไรขึ้นหากไม่มีการกำหนดตัวสร้างที่จะอนุญาตการอนุมานที่เหมาะสม คำตอบก็คือไม่มีการอนุมานได้รับอนุญาตเพราะการอนุมานจะถูกเรียกโดยโทรคอนสตรัค โดยไม่ต้องมีคอนสตรัคโทรมีไม่มีการอนุมาน
คล้ายกับการถามว่าfooมีการอนุมานเวอร์ชันใดที่นี่:
template <typename T> foo();
foo();
คำตอบคือรหัสนี้ผิดกฎหมายด้วยเหตุผลที่ระบุไว้
คำตอบของ MSalter
เท่าที่ฉันสามารถบอกได้คำตอบเดียวที่จะทำให้เกิดข้อกังวลที่ถูกต้องเกี่ยวกับคุณลักษณะที่เสนอ
ตัวอย่างคือ:
Variable var(num); // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?
คำถามสำคัญคือคอมไพลเลอร์เลือกตัวสร้างที่อนุมานประเภทที่นี่หรือตัวสร้างการคัดลอก ?
ลองใช้รหัสเราจะเห็นว่ามีการเลือกตัวสร้างการคัดลอก หากต้องการขยายตัวอย่าง :
Variable var(num); // infering ctor
Variable var2(var); // copy ctor
Variable var3(move(var)); // move ctor
// Variable var4(Variable(num)); // compiler error
ฉันไม่แน่ใจว่าข้อเสนอและมาตรฐานเวอร์ชันใหม่ระบุสิ่งนี้อย่างไร ดูเหมือนว่าจะถูกกำหนดโดย "คู่มือการหัก" ซึ่งเป็นมาตรฐานใหม่ที่ฉันยังไม่เข้าใจ
ฉันก็ไม่แน่ใจเหมือนกันว่าทำไมการvar4หักเงินจึงผิดกฎหมาย ข้อผิดพลาดของคอมไพเลอร์จาก g ++ ดูเหมือนจะบ่งชี้ว่าคำสั่งกำลังถูกแยกวิเคราะห์เป็นการประกาศฟังก์ชัน