ในยุคแห่งการรู้แจ้งของปี 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
และดังนั้นจึงแม่แบบประเภทของการเป็นpm
string
ยิ่งไปกว่านั้นสิ่งต่อไปนี้จะทำให้เกิดข้อผิดพลาดในการคอมไพล์เสมอ :
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 ++ ดูเหมือนจะบ่งชี้ว่าคำสั่งกำลังถูกแยกวิเคราะห์เป็นการประกาศฟังก์ชัน