การเชื่อมโยงวัตถุ C ++ 17, C ++ 14 และ C ++ 11 นั้นปลอดภัยหรือไม่


103

สมมติว่าฉันมีออบเจ็กต์ที่คอมไพล์สามชิ้นทั้งหมดสร้างโดยคอมไพเลอร์ / เวอร์ชันเดียวกัน :

  1. A ถูกรวบรวมด้วยมาตรฐาน C ++ 11
  2. B ถูกรวบรวมด้วยมาตรฐาน C ++ 14
  3. C ถูกคอมไพล์ด้วยมาตรฐาน C ++ 17

เพื่อความเรียบง่ายสมมติว่าส่วนหัวทั้งหมดเขียนด้วย C ++ 11 โดยใช้เฉพาะโครงสร้างที่ความหมายไม่ได้เปลี่ยนแปลงระหว่างเวอร์ชันมาตรฐานทั้งสามดังนั้นการพึ่งพาระหว่างกันจึงถูกแสดงอย่างถูกต้องด้วยการรวมส่วนหัวและคอมไพเลอร์ไม่คัดค้าน

การรวมกันของวัตถุเหล่านี้คืออะไรและไม่ปลอดภัยที่จะเชื่อมโยงเป็นไบนารีเดียว? ทำไม?


แก้ไข: ยินดีต้อนรับคำตอบที่ครอบคลุมคอมไพเลอร์หลัก (เช่น gcc, clang, vs ++)


6
ไม่ใช่คำถามเกี่ยวกับโรงเรียน / สัมภาษณ์ คำถามเกิดจากบางกรณี: ฉันกำลังทำโปรเจ็กต์ซึ่งขึ้นอยู่กับไลบรารีโอเพนซอร์ส ฉันสร้างไลบรารีนี้จากซอร์ส แต่ระบบการสร้างยอมรับเฉพาะแฟล็กเพื่อเลือกระหว่างอาคาร C ++ 03 / C ++ 11 คอมไพเลอร์ที่ฉันใช้รองรับมาตรฐานอื่น ๆ และฉันกำลังพิจารณาที่จะอัพเกรดโปรเจ็กต์ของตัวเองเป็น C ++ 17 ฉันไม่แน่ใจว่าเป็นการตัดสินใจที่ปลอดภัยหรือไม่ จะมีการแบ่ง ABI หรือวิธีอื่นที่ไม่แนะนำให้ใช้วิธีนี้ได้หรือไม่? ฉันไม่พบคำตอบที่ชัดเจนและตัดสินใจโพสต์คำถามเกี่ยวกับคดีทั่วไป
ricab

6
สิ่งนี้ขึ้นอยู่กับคอมไพเลอร์ทั้งหมด ไม่มีสิ่งใดในข้อกำหนด C ++ ที่เป็นทางการที่ควบคุมสถานการณ์นี้ นอกจากนี้ยังมีความเป็นไปได้เล็กน้อยที่โค้ดที่เขียนถึงมาตรฐาน C ++ 03 หรือ C + 11 จะมีปัญหาบางอย่างในระดับ C ++ 14 และ C ++ 17 ด้วยความรู้และประสบการณ์ที่เพียงพอ (และเริ่มเขียนโค้ดได้ดี) ควรจะแก้ไขปัญหาเหล่านี้ได้ อย่างไรก็ตามหากคุณไม่คุ้นเคยกับมาตรฐาน C ++ ที่ใหม่กว่าคุณควรยึดติดกับสิ่งที่ระบบบิลด์รองรับและได้รับการทดสอบแล้วว่าใช้งานได้
Sam Varshavchik

10
@Someprogrammerdude: เป็นคำถามที่คุ้มค่ามาก ฉันหวังว่าฉันจะมีคำตอบ สิ่งที่ฉันรู้ก็คือ libstdc ++ ผ่าน RHEL devtoolset นั้นเข้ากันได้กับการออกแบบโดยการเชื่อมโยงแบบคงที่ในสิ่งที่ใหม่กว่าและปล่อยให้สิ่งที่เก่ากว่าเพื่อแก้ไขแบบไดนามิกที่รันไทม์โดยใช้ libstdc ++ "เนทีฟ" ของ distro แต่นั่นไม่ได้ตอบคำถาม
Lightness Races ใน Orbit

4
@nm: ... ซึ่งส่วนใหญ่เป็นกรณี ... ทุกคนที่แจกจ่ายไลบรารี C ++ ที่ไม่กระจายการแจกจ่ายจะทำเช่นนั้น (1) ในรูปแบบไลบรารีไดนามิกและ (2) โดยไม่มีคอนเทนเนอร์ไลบรารีมาตรฐาน C ++ บนขอบเขตของอินเทอร์เฟซ ไลบรารีที่มาจากการแจกจ่าย Linux ทำได้ง่ายเนื่องจากสร้างขึ้นด้วยคอมไพเลอร์เดียวกันไลบรารีมาตรฐานเดียวกันและชุดแฟล็กเริ่มต้นที่เหมือนกัน
Matteo Italia

3
เพียงเพื่อชี้แจงความคิดเห็นก่อนหน้านี้จาก @MatteoItalia "และเมื่อเปลี่ยนจากโหมด C ++ 03 เป็น C ++ 11 (โดยเฉพาะอย่างยิ่ง std :: string)" นี้ไม่เป็นความจริงที่ใช้งานstd::stringการดำเนินงานใน libstdc ++ เป็นอิสระจาก-stdโหมดการใช้ นี่เป็นคุณสมบัติที่สำคัญอย่างแม่นยำเพื่อรองรับสถานการณ์เช่น OP คุณสามารถใช้รหัสใหม่std::stringในรหัส C ++ 03 และคุณสามารถใช้รหัสเก่าstd::stringในรหัส C ++ 11 ได้ (ดูลิงก์ในความคิดเห็นในภายหลังของ Matteo)
Jonathan Wakely

คำตอบ:


123

การรวมกันของวัตถุเหล่านี้คืออะไรและไม่ปลอดภัยที่จะเชื่อมโยงเป็นไบนารีเดียว? ทำไม?

สำหรับ GCCนั้นปลอดภัยที่จะเชื่อมโยงการรวมกันของวัตถุ A, B และ C เข้าด้วยกันหากสร้างด้วยเวอร์ชันเดียวกันทั้งหมดจะเข้ากันได้กับ ABI เวอร์ชันมาตรฐาน (เช่น-stdตัวเลือก) จะไม่สร้างความแตกต่าง

ทำไม? เพราะนั่นคือคุณสมบัติที่สำคัญของการนำไปใช้งานซึ่งเราพยายามอย่างเต็มที่เพื่อให้แน่ใจว่า

จุดที่คุณมีปัญหาคือถ้าคุณเชื่อมโยงอ็อบเจ็กต์ที่คอมไพล์กับ GCC เวอร์ชันต่างๆเข้าด้วยกันและคุณได้ใช้คุณสมบัติที่ไม่เสถียรจากมาตรฐาน C ++ ใหม่ก่อนที่การสนับสนุนของ GCC สำหรับมาตรฐานนั้นจะเสร็จสมบูรณ์ ตัวอย่างเช่นหากคุณรวบรวมวัตถุโดยใช้ GCC 4.9 -std=c++11และอีกวัตถุหนึ่งด้วย GCC 5 และ-std=c++11คุณจะมีปัญหา การสนับสนุน C ++ 11 เป็นการทดลองใน GCC 4.x ดังนั้นจึงมีการเปลี่ยนแปลงที่เข้ากันไม่ได้ระหว่างคุณลักษณะ C ++ 11 เวอร์ชัน GCC 4.9 และ 5 ในทำนองเดียวกันถ้าคุณรวบรวมวัตถุหนึ่งด้วย GCC 7 และ-std=c++17อีกวัตถุหนึ่งด้วย GCC 8 และ-std=c++17คุณจะมีปัญหาเนื่องจากการสนับสนุน C ++ 17 ใน GCC 7 และ 8 ยังคงอยู่ในการทดลองและพัฒนา

ในทางกลับกันการรวมกันของวัตถุต่อไปนี้จะใช้งานได้ (แม้ว่าจะดูหมายเหตุด้านล่างเกี่ยวกับlibstdc++.soเวอร์ชัน):

  • วัตถุ D คอมไพล์ด้วย GCC 4.9 และ -std=c++03
  • วัตถุ E คอมไพล์ด้วย GCC 5 และ -std=c++11
  • วัตถุ F คอมไพล์ด้วย GCC 7 และ -std=c++17

เนื่องจากการสนับสนุน C ++ 03 มีความเสถียรในคอมไพเลอร์ทั้งสามเวอร์ชันที่ใช้ดังนั้นส่วนประกอบ C ++ 03 จึงเข้ากันได้ระหว่างอ็อบเจ็กต์ทั้งหมด การสนับสนุน C ++ 11 มีความเสถียรตั้งแต่ GCC 5 แต่อ็อบเจกต์ D ไม่ได้ใช้คุณสมบัติ C ++ 11 ใด ๆ และอ็อบเจ็กต์ E และ F ทั้งสองใช้เวอร์ชันที่การสนับสนุน C ++ 11 เสถียร การสนับสนุน C ++ 17 ไม่เสถียรในเวอร์ชันคอมไพเลอร์ที่ใช้ แต่มีเพียงออบเจ็กต์ F เท่านั้นที่ใช้คุณสมบัติ C ++ 17 ดังนั้นจึงไม่มีปัญหาความเข้ากันได้กับอีกสองอ็อบเจ็กต์ (คุณลักษณะเดียวที่แชร์มาจาก C ++ 03 หรือ C ++ 11 และเวอร์ชันที่ใช้ทำให้ส่วนเหล่านั้นตกลง) หากคุณต้องการคอมไพล์อ็อบเจกต์ที่สี่ในภายหลัง G โดยใช้ GCC 8 -std=c++17จากนั้นคุณจะต้องคอมไพล์ F ใหม่ด้วยเวอร์ชันเดียวกัน (หรือไม่เชื่อมโยงไปยัง F) เนื่องจากสัญลักษณ์ C ++ 17 ใน F และ G ไม่เข้ากัน

ข้อแม้เดียวสำหรับความเข้ากันได้ที่อธิบายไว้ข้างต้นระหว่าง D, E และ F คือโปรแกรมของคุณต้องใช้libstdc++.soไลบรารีที่ใช้ร่วมกันจาก GCC 7 (หรือใหม่กว่า) เนื่องจากอ็อบเจ็กต์ F ถูกคอมไพล์ด้วย GCC 7 คุณจึงต้องใช้ไลบรารีที่ใช้ร่วมกันจากรีลีสนั้นเนื่องจากการคอมไพล์ส่วนใด ๆ ของโปรแกรมด้วย GCC 7 อาจทำให้เกิดการพึ่งพาสัญลักษณ์ที่ไม่มีอยู่ในlibstdc++.soจาก GCC 4.9 หรือ GCC 5 ในทำนองเดียวกัน หากคุณเชื่อมโยงกับวัตถุ G ซึ่งสร้างด้วย GCC 8 คุณจะต้องใช้libstdc++.soจาก GCC 8 เพื่อให้แน่ใจว่าพบสัญลักษณ์ทั้งหมดที่ต้องการโดย G กฎง่ายๆคือเพื่อให้แน่ใจว่าไลบรารีแบบแบ่งใช้ที่โปรแกรมใช้ในรันไทม์เป็นอย่างน้อยใหม่เท่ากับเวอร์ชันที่ใช้คอมไพล์อ็อบเจ็กต์ใด ๆ

ข้อแม้อีกประการหนึ่งเมื่อใช้ GCC ซึ่งกล่าวถึงแล้วในความคิดเห็นในคำถามของคุณคือเนื่องจาก GCC 5 มีการใช้งานสองแบบที่std::stringมีอยู่ใน libstdc ++ การใช้งานทั้งสองไม่เข้ากันได้กับลิงก์ (มีชื่อที่แตกต่างกันดังนั้นจึงไม่สามารถเชื่อมโยงเข้าด้วยกันได้) แต่สามารถอยู่ร่วมกันในไบนารีเดียวกันได้ (มีชื่อที่แตกต่างกันดังนั้นอย่าขัดแย้งกันหากวัตถุหนึ่งใช้std::stringและ การใช้งานอื่น ๆstd::__cxx11::string) หากออบเจ็กต์ของคุณใช้std::stringโดยปกติแล้วพวกเขาควรจะคอมไพล์ด้วยการใช้งานสตริงเดียวกัน รวบรวม-D_GLIBCXX_USE_CXX11_ABI=0เพื่อเลือกgcc4-compatibleการนำไปใช้งานเดิมหรือ-D_GLIBCXX_USE_CXX11_ABI=1เพื่อเลือกการcxx11ใช้งานใหม่(อย่าหลงกลชื่อมันสามารถใช้ใน C ++ 03 ได้เช่นกันเรียกว่าcxx11เนื่องจากเป็นไปตามข้อกำหนด C ++ 11) การใช้งานใดเป็นค่าเริ่มต้นขึ้นอยู่กับวิธีการกำหนดค่า GCC แต่ค่าเริ่มต้นสามารถถูกแทนที่ได้ตลอดเวลาที่คอมไพล์ด้วยมาโคร


"เนื่องจากการรวบรวมส่วนใดส่วนหนึ่งของโปรแกรมด้วย GCC 7 อาจทำให้เกิดการอ้างอิงกับสัญลักษณ์ที่มีอยู่ใน libstdc ++ ดังนั้นจาก GCC 4.9 หรือ GCC 5" คุณหมายความว่าไม่ได้อยู่ใน GCC 4.9 หรือ GCC 5 ใช่ไหม สิ่งนี้ใช้กับการลิงก์แบบคงที่ด้วยหรือไม่ ขอขอบคุณสำหรับข้อมูลเกี่ยวกับความเข้ากันได้ในเวอร์ชันคอมไพเลอร์
Hadi Brais

1
ฉันเพิ่งตระหนักถึงข้อบกพร่องที่ยิ่งใหญ่ในการให้รางวัลกับคำถามนี้ 😂
Lightness Races ในวงโคจร

4
@ricab ฉันแน่ใจ 90% ว่าคำตอบเหมือนกันสำหรับ Clang / libc ++ แต่ฉันไม่รู้เกี่ยวกับ MSVC
Jonathan Wakely

1
คำตอบนี้เป็นตัวเอก มีการบันทึกไว้ที่ไหนสักแห่งว่า 5.0+ เสถียรสำหรับ 11/14 หรือไม่?
Barry

1
ไม่ชัดเจนมากหรือในที่เดียว gcc.gnu.org/gcc-5/changes.html#libstdcxxและgcc.gnu.org/onlinedocs/libstdc++/manual/api.html#api.rel_51ประกาศการสนับสนุนไลบรารีสำหรับ C ++ 11 ให้สมบูรณ์ (ภาษา การสนับสนุนเป็นคุณลักษณะที่สมบูรณ์ก่อนหน้านี้ แต่ยังคงเป็น "ทดลอง") การสนับสนุนไลบรารี C ++ 14 ยังคงอยู่ในรายการทดลองจนถึง 6.1 แต่ในทางปฏิบัติไม่มีอะไรเปลี่ยนแปลงระหว่าง 5.x และ 6.x ที่มีผลต่อ ABI
Jonathan Wakely

17

คำตอบมีสองส่วน ความเข้ากันได้ในระดับคอมไพเลอร์และความเข้ากันได้ที่ระดับตัวเชื่อมโยง เริ่มต้นด้วยอดีต

สมมติว่าส่วนหัวทั้งหมดเขียนด้วย 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__

// Maybe defined as a field in a template or a type.
int x[2] = { M(1'2,3'4) };

พิจารณาหน่วยการแปลสองหน่วยที่มีไฟล์ส่วนหัว แต่กำหนดเป้าหมาย C ++ 11 และ C ++ 14 ตามลำดับ เมื่อกำหนดเป้าหมาย C ++ 11 เครื่องหมายจุลภาคภายในเครื่องหมายคำพูดจะไม่ถือว่าเป็นตัวคั่นพารามิเตอร์ มีพารามิเตอร์เพียงครั้งเดียว ดังนั้นรหัสจะเทียบเท่ากับ:

int x[2] = { 0 }; // C++11

ในทางกลับกันเมื่อกำหนดเป้าหมาย C ++ 14 เครื่องหมายคำพูดเดี่ยวจะถูกตีความว่าเป็นตัวคั่นหลัก ดังนั้นรหัสจะเทียบเท่ากับ:

int x[2] = { 34, 0 }; // C++14 and C++17

ประเด็นก็คือการใช้เครื่องหมายคำพูดเดี่ยวในไฟล์ส่วนหัว 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: คำตอบของฉันอาจไม่สมบูรณ์หรือแม่นยำมาก


คำถามนี้มีจุดประสงค์เพื่อให้เกี่ยวข้องกับการเชื่อมโยงมากกว่าการรวบรวม ฉันรับรู้ (ขอบคุณสำหรับความคิดเห็นนี้ ) ที่อาจไม่ชัดเจนและได้แก้ไขเพื่อให้ชัดเจนว่าส่วนหัวใด ๆ ที่รวมมีการตีความเหมือนกันในทั้งสามมาตรฐาน
ricab

@ricab คำตอบครอบคลุมทั้งการรวบรวมและการเชื่อมโยง ฉันคิดว่าคุณกำลังถามเกี่ยวกับทั้งสองอย่าง
Hadi Brais

1
แน่นอน แต่ฉันพบว่าคำตอบนั้นยาวเกินไปและสับสนโดยเฉพาะอย่างยิ่งจนกระทั่ง "ตอนนี้ฉันจะพูดถึงความเข้ากันได้ในระดับตัวเชื่อมโยง" คุณสามารถเปลี่ยนทุกอย่างข้างต้นว่ามีบางสิ่งบางอย่างเช่นถ้าส่วนหัวรวมถึงไม่สามารถกล่าวอ้างที่จะมีความหมายเหมือนกันใน C ++ 11 และ C ++ 14/17 แล้วมันไม่ปลอดภัยที่จะรวมไว้ในสถานที่แรก สำหรับส่วนที่เหลือคุณมีแหล่งที่มาที่แสดงว่าสัญลักษณ์แสดงหัวข้อย่อยทั้งสามนี้เป็นสาเหตุเดียวที่ทำให้เข้ากันไม่ได้หรือไม่? ขอบคุณสำหรับคำตอบไม่ว่าในกรณีใดฉันยังคงโหวตอยู่
ricab

@ricab ฉันไม่สามารถพูดได้อย่างแน่นอน นั่นเป็นเหตุผลที่ฉันเพิ่มข้อแม้ในตอนท้ายของคำตอบ ใครก็ตามสามารถขยายคำตอบเพื่อให้แม่นยำยิ่งขึ้นหรือสมบูรณ์ในกรณีที่ฉันพลาดอะไรไป
Hadi Brais

สิ่งนี้ทำให้ฉันสับสน: "การใช้คอมไพเลอร์เดียวกันหมายความว่าจะใช้ส่วนหัวไลบรารีมาตรฐานเดียวกันและซอร์สไฟล์ (... )" จะเป็นเช่นนั้นได้อย่างไร? หากฉันมีโค้ดเก่าที่คอมไพล์ด้วย gcc5 'ไฟล์คอมไพเลอร์' ที่เป็นของเวอร์ชันนั้นจะไม่สามารถพิสูจน์ได้ในอนาคต สำหรับซอร์สโค้ดที่คอมไพล์ในเวลาที่ต่างกันกับเวอร์ชันคอมไพเลอร์ที่แตกต่างกันเราค่อนข้างมั่นใจได้ว่าส่วนหัวของไลบรารีและไฟล์ต้นฉบับนั้นแตกต่างกัน ด้วยกฎของคุณที่ว่าสิ่งเหล่านี้ควรเหมือนกันคุณต้องคอมไพล์ซอร์สโค้ดรุ่นเก่าอีกครั้งด้วย gcc5, ... และตรวจสอบให้แน่ใจว่าทั้งหมดใช้ 'ไฟล์คอมไพเลอร์' ล่าสุด (เดียวกัน)
user2943111

2

มาตรฐาน C ++ ใหม่แบ่งออกเป็นสองส่วน ได้แก่ คุณลักษณะภาษาและส่วนประกอบไลบรารีมาตรฐาน

ตามที่คุณหมายถึงมาตรฐานใหม่การเปลี่ยนแปลงในภาษาเอง (เช่น ranged-for) แทบจะไม่มีปัญหา (บางครั้งความขัดแย้งจะเกิดขึ้นในส่วนหัวของไลบรารีของบุคคลที่สามที่มีคุณลักษณะภาษามาตรฐานใหม่กว่า)

แต่ห้องสมุดมาตรฐาน ...

คอมไพเลอร์แต่ละเวอร์ชันมาพร้อมกับการใช้งานไลบรารีมาตรฐาน C ++ (libstdc ++ พร้อม gcc, libc ++ พร้อมเสียงดัง, ไลบรารีมาตรฐาน MS C ++ พร้อม VC ++, ... ) นอกจากนี้ในบางกรณีคุณอาจใช้ไลบรารีมาตรฐานอื่นนอกเหนือจากที่คอมไพเลอร์ให้มา สิ่งที่คุณควรดูแลคือการเชื่อมโยงการใช้งานไลบรารีมาตรฐานรุ่นเก่ากับใหม่กว่า

ข้อขัดแย้งที่อาจเกิดขึ้นระหว่างไลบรารีของบุคคลที่สามและรหัสของคุณคือไลบรารีมาตรฐาน (และไลบรารีอื่น ๆ ) ที่เชื่อมโยงไปยังไลบรารีของบุคคลที่สามนั้น


"คอมไพเลอร์แต่ละเวอร์ชันมาพร้อมกับการใช้งาน STL"ไม่พวกเขาทำไม่ได้
Lightness Races in Orbit

@LightnessRacesinOrbit คุณหมายความว่าไม่มีการปันส่วนระหว่างเช่น libstdc ++ และ gcc?
E.Vakili

9
ไม่ฉันหมายความว่า STL ล้าสมัยอย่างมีประสิทธิภาพมานานกว่ายี่สิบปีแล้ว คุณหมายถึงไลบรารีมาตรฐาน C ++ สำหรับคำตอบที่เหลือคุณสามารถให้ข้อมูลอ้างอิง / หลักฐานเพื่อสำรองการอ้างสิทธิ์ของคุณได้หรือไม่? ฉันคิดว่าสำหรับคำถามเช่นนี้มันสำคัญ
Lightness Races ใน Orbit

3
ขออภัยไม่ไม่ชัดเจนจากข้อความ คุณได้ทำการยืนยันที่น่าสนใจ แต่ยังไม่ได้สำรองข้อมูลด้วยหลักฐานใด ๆ
Lightness Races ใน Orbit
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.