การอัปเดต C ++ 17
ใน C ++ 17 ความหมายของการA_factory_func()
เปลี่ยนจากการสร้างวัตถุชั่วคราว (C ++ <= 14) เป็นเพียงการระบุการกำหนดค่าเริ่มต้นของวัตถุใดก็ตามที่นิพจน์นี้ถูกกำหนดค่าเริ่มต้นเป็น (พูดอย่างหลวม ๆ ) ใน C ++ 17 วัตถุเหล่านี้ (เรียกว่า "วัตถุผลลัพธ์") เป็นตัวแปรที่สร้างขึ้นโดยการประกาศ (เหมือนa1
) วัตถุประดิษฐ์ที่สร้างขึ้นเมื่อการเริ่มต้นสิ้นสุดลงด้วยการถูกทิ้งหรือหากวัตถุนั้นจำเป็นสำหรับการอ้างอิงอ้างอิง (เช่นในA_factory_func();
กรณีสุดท้าย วัตถุถูกสร้างขึ้นเทียมเรียกว่า "การกลายเป็นวัตถุชั่วคราว" เนื่องจากA_factory_func()
ไม่มีตัวแปรหรือการอ้างอิงที่จะต้องมีวัตถุอยู่)
เป็นตัวอย่างในกรณีของเราในกรณีของa1
และa2
กฎพิเศษกล่าวว่าในการประกาศดังกล่าววัตถุผลของการเริ่มต้น prvalue ประเภทเดียวกันเป็นa1
ตัวแปรa1
และดังนั้นจึงA_factory_func()
เริ่มต้นวัตถุa1
โดยตรง การทำงานแบบคนกลางจะไม่ส่งผลใด ๆ เพราะA_factory_func(another-prvalue)
เพียงแค่ "ส่งผ่าน" วัตถุผลลัพธ์ของค่า prvalue ด้านนอกเพื่อให้เป็นวัตถุผลลัพธ์ของ prvalue ด้านใน
A a1 = A_factory_func();
A a2(A_factory_func());
ขึ้นอยู่กับประเภทA_factory_func()
ผลตอบแทน ฉันคิดว่ามันส่งกลับA
- แล้วก็ทำแบบเดียวกัน - ยกเว้นว่าเมื่อตัวสร้างการคัดลอกชัดเจนแล้วคนแรกจะล้มเหลว อ่าน8.6 / 14
double b1 = 0.5;
double b2(0.5);
สิ่งนี้ทำเช่นเดียวกันเพราะเป็นชนิดในตัว (ซึ่งหมายความว่าไม่ใช่ประเภทของชั้นเรียนที่นี่) อ่าน8.6 / 14
A c1;
A c2 = A();
A c3(A());
สิ่งนี้ไม่ได้ทำแบบเดียวกัน การเริ่มต้นครั้งแรกเริ่มต้นถ้าA
ไม่ใช่ POD และไม่ทำการเริ่มต้นใด ๆ สำหรับ POD (อ่าน8.6 / 9 ) สำเนาที่สองเริ่มต้น: ค่าเริ่มต้นชั่วคราวแล้วคัดลอกค่านั้นลงในc2
(อ่าน5.2.3 / 2และ8.6 / 14 ) หลักสูตรนี้จะต้องมีตัวสร้างสำเนาที่ไม่ชัดเจน (อ่าน8.6 / 14และ12.3.1 / 3และ13.3.1.3/1 ) ที่สามสร้างการประกาศฟังก์ชั่นสำหรับฟังก์ชั่นc3
ที่ส่งกลับA
และที่จะใช้ฟังก์ชั่นตัวชี้ไปยังฟังก์ชั่นกลับA
(อ่าน8.2 )
การเจาะลึกการกำหนดค่าเริ่มต้นโดยตรงและคัดลอกการเริ่มต้น
ในขณะที่พวกเขาดูเหมือนกันและควรจะทำแบบเดียวกันทั้งสองรูปแบบมีความแตกต่างอย่างน่าทึ่งในบางกรณี การกำหนดค่าเริ่มต้นสองรูปแบบนั้นโดยตรงและคัดลอกการเริ่มต้น:
T t(x);
T t = x;
มีพฤติกรรมที่เราสามารถระบุคุณลักษณะแต่ละรายการได้:
- พฤติกรรมการเริ่มต้นตรงเช่นการเรียกฟังก์ชั่นการทำงานที่มากเกินไป: ฟังก์ชั่นในกรณีนี้มีการก่อสร้างของ
T
(รวมทั้งexplicit
คน) x
และอาร์กิวเมนต์เป็น ความละเอียดเกินพิกัดจะค้นหาตัวสร้างที่ตรงกันที่สุดและเมื่อจำเป็นจะทำการแปลงที่จำเป็น
- คัดลอกการเริ่มต้นสร้างลำดับการแปลงนัย: มันพยายามที่จะแปลงไปยังวัตถุของการพิมพ์
x
T
(จากนั้นอาจคัดลอกวัตถุนั้นลงในวัตถุที่จะเริ่มต้นได้ดังนั้นตัวสร้างสำเนาก็จำเป็นเช่นกัน - แต่นี่ไม่ใช่สิ่งสำคัญด้านล่าง)
ดังที่คุณเห็นการเริ่มต้นการคัดลอกเป็นส่วนหนึ่งของการเริ่มต้นโดยตรงในแง่ของการแปลงโดยนัยที่เป็นไปได้: ในขณะที่การเริ่มต้นโดยตรงมีตัวสร้างทั้งหมดที่สามารถโทรได้และนอกจากนี้ยังสามารถทำการแปลงโดยนัย สามารถตั้งค่าลำดับการแปลงโดยนัยได้หนึ่งชุด
ฉันพยายามอย่างหนักและได้รหัสต่อไปนี้เพื่อส่งข้อความที่แตกต่างกันสำหรับแต่ละรูปแบบเหล่านั้นโดยไม่ต้องใช้ "ชัดเจน" ผ่านตัวexplicit
สร้าง
#include <iostream>
struct B;
struct A {
operator B();
};
struct B {
B() { }
B(A const&) { std::cout << "<direct> "; }
};
A::operator B() { std::cout << "<copy> "; return B(); }
int main() {
A a;
B b1(a); // 1)
B b2 = a; // 2)
}
// output: <direct> <copy>
มันทำงานอย่างไรและทำไมมันถึงออกผลลัพธ์นั้น
การเริ่มต้นโดยตรง
ก่อนอื่นไม่รู้เกี่ยวกับการแปลง มันจะพยายามเรียกตัวสร้าง ในกรณีนี้ตัวสร้างต่อไปนี้พร้อมใช้งานและเป็นการจับคู่ที่ตรงกัน :
B(A const&)
มีการแปลงไม่น้อยกว่าการแปลงที่กำหนดโดยผู้ใช้จำเป็นต้องเรียกคอนสตรัคเตอร์นั้น (โปรดทราบว่าไม่มีการแปลงคุณสมบัติแบบ const ที่นี่) และการเริ่มต้นโดยตรงจะเรียกมันว่า
คัดลอกการเริ่มต้น
ดังกล่าวข้างต้นการเริ่มต้นคัดลอกจะสร้างลำดับการแปลงเมื่อa
ไม่ได้พิมพ์B
หรือได้รับมาจากมัน (ซึ่งเป็นกรณีที่ชัดเจนที่นี่) ดังนั้นมันจะมองหาวิธีที่จะทำการแปลงและจะหาผู้สมัครดังต่อไปนี้
B(A const&)
operator B(A&);
สังเกตว่าฉันเขียนฟังก์ชั่นการแปลงใหม่ได้อย่างไร: ประเภทพารามิเตอร์สะท้อนให้เห็นถึงประเภทของthis
ตัวชี้ซึ่งในฟังก์ชั่นที่ไม่ใช่สมาชิก const จะไม่ใช่แบบ const ตอนนี้เราเรียกผู้สมัครเหล่านี้x
ว่าเป็นข้อโต้แย้ง ผู้ชนะคือฟังก์ชั่นการแปลง: เพราะถ้าเรามีสองฟังก์ชั่นผู้สมัครทั้งสองยอมรับการอ้างอิงประเภทเดียวกันแล้วรุ่นconst น้อยชนะ (นี่คือโดยวิธีการยังกลไกที่ชอบฟังก์ชั่นที่ไม่ใช่สมาชิก const เรียกร้องให้ไม่ใช่ วัตถุ -const)
โปรดทราบว่าหากเราเปลี่ยนฟังก์ชั่นการแปลงให้เป็นฟังก์ชั่นสมาชิก const การแปลงนั้นจะคลุมเครือ (เพราะทั้งคู่มีประเภทพารามิเตอร์ที่A const&
แล้ว): คอมไพเลอร์ Comeau ปฏิเสธอย่างถูกต้อง แต่ GCC ยอมรับมันในโหมดที่ไม่ใช่ทางตัน -pedantic
แม้ว่าการเปลี่ยนไปใช้จะทำให้มันส่งสัญญาณเตือนความกำกวมที่เหมาะสมเช่นกัน
ฉันหวังว่านี่จะช่วยให้ชัดเจนขึ้นว่าแบบฟอร์มทั้งสองแตกต่างกันอย่างไร!
A c1; A c2 = c1; A c3(c1);
-