คำตอบมีสองส่วน ความเข้ากันได้ในระดับคอมไพเลอร์และความเข้ากันได้ที่ระดับตัวเชื่อมโยง เริ่มต้นด้วยอดีต
สมมติว่าส่วนหัวทั้งหมดเขียนด้วย C ++ 11
การใช้คอมไพเลอร์เดียวกันหมายความว่าส่วนหัวไลบรารีมาตรฐานเดียวกันและไฟล์ต้นฉบับ (หน่วยที่เชื่อมโยงกับคอมไพเลอร์) จะถูกใช้โดยไม่คำนึงถึงมาตรฐาน C ++ ดังนั้นไฟล์ส่วนหัวของไลบรารีมาตรฐานจึงถูกเขียนขึ้นเพื่อให้เข้ากันได้กับเวอร์ชัน C ++ ทั้งหมดที่คอมไพเลอร์สนับสนุน
ที่กล่าวว่าหากตัวเลือกคอมไพเลอร์ที่ใช้ในการคอมไพล์หน่วยการแปลระบุมาตรฐาน C ++ ที่เฉพาะเจาะจงคุณสมบัติใด ๆ ที่มีเฉพาะในมาตรฐานที่ใหม่กว่าไม่ควรเข้าถึงได้ สิ่งนี้ทำได้โดยใช้__cplusplus
คำสั่ง ดูไฟล์ต้นฉบับเวกเตอร์สำหรับตัวอย่างที่น่าสนใจเกี่ยวกับวิธีการใช้งาน ในทำนองเดียวกันคอมไพเลอร์จะปฏิเสธคุณลักษณะทางวากยสัมพันธ์ใด ๆ ที่นำเสนอโดยมาตรฐานเวอร์ชันใหม่กว่า
ทั้งหมดนี้หมายความว่าข้อสันนิษฐานของคุณใช้ได้กับไฟล์ส่วนหัวที่คุณเขียนเท่านั้น ไฟล์ส่วนหัวเหล่านี้อาจทำให้เกิดความไม่เข้ากันได้เมื่อรวมอยู่ในหน่วยการแปลที่แตกต่างกันซึ่งกำหนดเป้าหมายตามมาตรฐาน C ++ ที่แตกต่างกัน สิ่งนี้จะกล่าวถึงในภาคผนวก C ของมาตรฐาน C ++ มี 4 ข้อฉันจะพูดถึงข้อแรกเท่านั้นและจะกล่าวถึงส่วนที่เหลือสั้น ๆ
ค. 3.1 ข้อ 2: อนุสัญญาศัพท์
อัญประกาศเดี่ยวคั่นลิเทอรัลอักขระใน C ++ 11 ในขณะที่เป็นตัวคั่นหลักใน C ++ 14 และ C ++ 17 สมมติว่าคุณมีข้อกำหนดมาโครต่อไปนี้ในไฟล์ส่วนหัว C ++ 11 แท้:
#define M(x, ...) __VA_ARGS__
int x[2] = { M(1'2,3'4) };
พิจารณาหน่วยการแปลสองหน่วยที่มีไฟล์ส่วนหัว แต่กำหนดเป้าหมาย C ++ 11 และ C ++ 14 ตามลำดับ เมื่อกำหนดเป้าหมาย C ++ 11 เครื่องหมายจุลภาคภายในเครื่องหมายคำพูดจะไม่ถือว่าเป็นตัวคั่นพารามิเตอร์ มีพารามิเตอร์เพียงครั้งเดียว ดังนั้นรหัสจะเทียบเท่ากับ:
int x[2] = { 0 };
ในทางกลับกันเมื่อกำหนดเป้าหมาย C ++ 14 เครื่องหมายคำพูดเดี่ยวจะถูกตีความว่าเป็นตัวคั่นหลัก ดังนั้นรหัสจะเทียบเท่ากับ:
int x[2] = { 34, 0 };
ประเด็นก็คือการใช้เครื่องหมายคำพูดเดี่ยวในไฟล์ส่วนหัว C ++ 11 ที่แท้จริงอาจทำให้เกิดข้อบกพร่องที่น่าแปลกใจในหน่วยการแปลที่กำหนดเป้าหมาย C ++ 14/17 ดังนั้นแม้ว่าไฟล์ส่วนหัวจะเขียนด้วย C ++ 11 แต่ก็ต้องเขียนอย่างระมัดระวังเพื่อให้แน่ใจว่าเข้ากันได้กับมาตรฐานรุ่นที่ใหม่กว่า __cplusplus
สั่งอาจจะมีประโยชน์ที่นี่
อีกสามข้อจากมาตรฐาน ได้แก่ :
C.3.2 ข้อ 3: แนวคิดพื้นฐาน
การเปลี่ยนแปลง : deallocator ปกติ (ไม่ใช่ตำแหน่ง) ใหม่
เหตุผล : จำเป็นสำหรับการจัดสรรขนาด
ผลกระทบต่อคุณลักษณะดั้งเดิม : รหัส C ++ 2011 ที่ถูกต้องสามารถประกาศฟังก์ชันการจัดสรรตำแหน่งส่วนกลางและฟังก์ชันการยกเลิกการจัดสรรได้ดังนี้:
void operator new(std::size_t, std::size_t);
void operator delete(void*, std::size_t) noexcept;
อย่างไรก็ตามในมาตรฐานสากลฉบับนี้การประกาศการลบตัวดำเนินการอาจตรงกับการลบตัวดำเนินการตามปกติ (ไม่ใช่ตำแหน่ง) ที่กำหนดไว้ล่วงหน้า (3.7.4) หากเป็นเช่นนั้นโปรแกรมจะมีรูปแบบไม่ถูกต้องเช่นเดียวกับฟังก์ชันการจัดสรรสมาชิกคลาสและฟังก์ชันการจัดสรร (5.3.4)
C.3.3 ข้อ 7: การประกาศ
การเปลี่ยนแปลง : ฟังก์ชันสมาชิกที่ไม่คงที่ constexpr ไม่ใช่ฟังก์ชันสมาชิก const โดยปริยาย
เหตุผล : จำเป็นเพื่อให้ฟังก์ชันสมาชิก constexpr กลายพันธุ์วัตถุ
ผลกระทบต่อคุณลักษณะดั้งเดิม : โค้ด C ++ 2011 ที่ถูกต้องอาจไม่สามารถรวบรวมในมาตรฐานสากลนี้ได้
ตัวอย่างเช่นรหัสต่อไปนี้ใช้ได้ใน C ++ 2011 แต่ไม่ถูกต้องในมาตรฐานสากลนี้เนื่องจากมีการประกาศฟังก์ชันสมาชิกเดียวกันสองครั้งโดยมีประเภทผลตอบแทนที่แตกต่างกัน:
struct S {
constexpr const int &f();
int &f();
};
C.3.4 ข้อ 27: ไลบรารีอินพุต / เอาต์พุต
การเปลี่ยนแปลง : ไม่ได้กำหนดได้รับ
เหตุผล : การใช้สิ่งที่ได้รับถือเป็นอันตราย
ผลกระทบต่อคุณลักษณะดั้งเดิม : รหัส C ++ 2011 ที่ถูกต้องที่ใช้ฟังก์ชัน gets อาจไม่สามารถรวบรวมในมาตรฐานสากลนี้ได้
ความเข้ากันไม่ได้ที่อาจเกิดขึ้นระหว่าง C ++ 14 และ C ++ 17 จะกล่าวถึงใน C.4 เนื่องจากไฟล์ส่วนหัวที่ไม่ได้มาตรฐานทั้งหมดเขียนด้วย C ++ 11 (ตามที่ระบุไว้ในคำถาม) ปัญหาเหล่านี้จะไม่เกิดขึ้นดังนั้นฉันจะไม่กล่าวถึงในที่นี้
ตอนนี้ฉันจะพูดถึงความเข้ากันได้ในระดับตัวเชื่อมโยง โดยทั่วไปสาเหตุที่เป็นไปได้ของความเข้ากันไม่ได้มีดังต่อไปนี้:
หากรูปแบบของไฟล์อ็อบเจ็กต์ผลลัพธ์ขึ้นอยู่กับมาตรฐาน C ++ เป้าหมายผู้เชื่อมโยงต้องสามารถเชื่อมโยงไฟล์อ็อบเจ็กต์ต่างๆ ใน GCC, LLVM และ VC ++ โชคดีที่ไม่เป็นเช่นนั้น นั่นคือรูปแบบของไฟล์อ็อบเจ็กต์จะเหมือนกันโดยไม่คำนึงถึงมาตรฐานเป้าหมายแม้ว่าจะขึ้นอยู่กับคอมไพเลอร์เองก็ตาม ในความเป็นจริงไม่มีผู้เชื่อมโยง GCC, LLVM และ VC ++ ใดที่ต้องการความรู้เกี่ยวกับมาตรฐาน C ++ เป้าหมาย นอกจากนี้ยังหมายความว่าเราสามารถเชื่อมโยงอ็อบเจ็กต์ไฟล์ที่คอมไพล์แล้ว (ลิงก์รันไทม์แบบคงที่)
หากรูทีนการเริ่มต้นโปรแกรม (ฟังก์ชันที่เรียกใช้main
) แตกต่างกันสำหรับมาตรฐาน C ++ ที่แตกต่างกันและรูทีนที่แตกต่างกันไม่สามารถทำงานร่วมกันได้ก็จะไม่สามารถเชื่อมโยงไฟล์ออบเจ็กต์ได้ ใน GCC, LLVM และ VC ++ โชคดีที่ไม่เป็นเช่นนั้น นอกจากนี้ลายเซ็นของmain
ฟังก์ชัน (และข้อ จำกัด ที่ใช้กับมันโปรดดูมาตรา 3.6 ของมาตรฐาน) จะเหมือนกันในมาตรฐาน C ++ ทั้งหมดดังนั้นจึงไม่สำคัญว่าจะมีหน่วยการแปลใดอยู่
โดยทั่วไป WPO อาจทำงานได้ไม่ดีกับไฟล์อ็อบเจ็กต์ที่คอมไพล์โดยใช้มาตรฐาน C ++ ที่แตกต่างกัน สิ่งนี้ขึ้นอยู่กับว่าขั้นตอนใดของคอมไพเลอร์ต้องการความรู้เกี่ยวกับมาตรฐานเป้าหมายและขั้นตอนใดที่ไม่มีและผลกระทบที่มีต่อการเพิ่มประสิทธิภาพระหว่างขั้นตอนที่ข้ามไฟล์ออบเจ็กต์ โชคดีที่ GCC, LLVM และ VC ++ ได้รับการออกแบบมาอย่างดีและไม่มีปัญหานี้ (ไม่ใช่ที่ฉันรู้)
ดังนั้น GCC, LLVM และ VC ++ จึงได้รับการออกแบบมาเพื่อเปิดใช้งานความเข้ากันได้แบบไบนารีในมาตรฐาน C ++ เวอร์ชันต่างๆ นี่ไม่ใช่ข้อกำหนดของมาตรฐานจริงๆ
อย่างไรก็ตามแม้ว่าคอมไพเลอร์ VC ++ จะมีสวิตช์ stdซึ่งช่วยให้คุณสามารถกำหนดเป้าหมายมาตรฐาน C ++ เวอร์ชันใดเวอร์ชันหนึ่งได้ แต่ก็ไม่สนับสนุนการกำหนดเป้าหมาย C ++ 11 เวอร์ชันขั้นต่ำที่สามารถระบุได้คือ C ++ 14 ซึ่งเป็นค่าเริ่มต้นที่เริ่มต้นจาก Visual C ++ 2013 Update 3 คุณสามารถใช้ VC ++ เวอร์ชันเก่าเพื่อกำหนดเป้าหมาย C ++ 11 ได้ แต่คุณจะต้องใช้คอมไพเลอร์ VC ++ อื่น เพื่อรวบรวมหน่วยการแปลที่แตกต่างกันซึ่งกำหนดเป้าหมายเวอร์ชันต่างๆของมาตรฐาน C ++ ซึ่งอย่างน้อยที่สุดจะทำลาย WPO
CAVEAT: คำตอบของฉันอาจไม่สมบูรณ์หรือแม่นยำมาก