บรรลุความเข้ากันได้กับ C ++ 11


12

ฉันทำงานกับแอพพลิเคชั่นซอฟต์แวร์ขนาดใหญ่ที่ต้องใช้งานบนหลายแพลตฟอร์ม บางแพลตฟอร์มเหล่านี้รองรับคุณสมบัติบางอย่างของ C ++ 11 (เช่น MSVS 2010) และบางแพลตฟอร์มไม่รองรับ (เช่น GCC 4.3.x) ฉันคาดว่าสถานการณ์นี้จะดำเนินต่อไปอีกหลายปี (คาดเดาที่ดีที่สุดของฉัน: 3-5 ปี)

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

เริ่มต้นด้วย std :: move () วิธีที่ชัดเจนที่สุดในการบรรลุความเข้ากันได้คือการใส่บางอย่างในไฟล์ส่วนหัวร่วม:

#if !defined(HAS_STD_MOVE)
namespace std { // C++11 emulation
  template <typename T> inline T& move(T& v) { return v; }
  template <typename T> inline const T& move(const T& v) { return v; }
}
#endif // !defined(HAS_STD_MOVE)

สิ่งนี้ทำให้ผู้คนสามารถเขียนสิ่งต่าง ๆ เช่น

std::vector<Thing> x = std::move(y);

... ด้วยการไม่ต้องรับโทษ มันทำสิ่งที่พวกเขาต้องการใน C ++ 11 และทำได้ดีที่สุดใน C ++ 03 เมื่อในที่สุดเราก็วางคอมไพเลอร์ C ++ 03 ตัวสุดท้ายรหัสนี้จะยังคงเหมือนเดิม

อย่างไรก็ตามตามมาตรฐานมันผิดกฎหมายที่จะฉีดสัญลักษณ์ใหม่เข้าไปในstdnamespace นั่นคือทฤษฎี คำถามของฉันคือการพูดในทางปฏิบัติจะมีอันตรายใด ๆ ในการทำเช่นนี้เป็นวิธีในการบรรลุความเข้ากันได้ไปข้างหน้า?


1
Boost ให้บริการแล้วค่อนข้างน้อยและมีรหัสให้ใช้ฟีเจอร์ใหม่เมื่อ / ที่ไหนพร้อมดังนั้นคุณอาจจะสามารถใช้สิ่งที่ Boost จัดเตรียมไว้และทำได้ด้วย แน่นอนว่ามีข้อ จำกัด - คุณสมบัติใหม่ส่วนใหญ่ถูกเพิ่มเข้ามาโดยเฉพาะเนื่องจากโซลูชันบนห้องสมุดไม่เพียงพอ
Jerry Coffin

ใช่ฉันคิดอย่างเฉพาะเจาะจงเกี่ยวกับฟีเจอร์เหล่านั้นที่สามารถนำไปใช้ในระดับห้องสมุดไม่ใช่การเปลี่ยนแปลงทางไวยากรณ์ Boost ไม่ได้แก้ไขปัญหาของความเข้ากันได้ (ต่อเนื่อง) ของการส่งต่อ เว้นแต่ว่าฉันจะหายไปบางสิ่งบางอย่าง ...
mcmcc

Gcc 4.3 มีคุณสมบัติ C ++ 11 อยู่แล้วจำนวนมากการอ้างอิงค่า Rvalue อาจเป็นสิ่งที่สำคัญที่สุด
Jan Hudec

@JanHudec: ถูกต้อง ตัวอย่างที่ไม่ดี ไม่ว่าในกรณีใด ๆ มีคอมไพเลอร์อื่น ๆ ที่ไม่สนับสนุนไวยากรณ์อย่างแน่นอน (เช่นเวอร์ชันใด ๆ ของคอมไพเลอร์ C ++ ของ IBM ที่เรามี)
mcmcc

คำตอบ:


9

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

มีคุณสมบัติที่ดีมากมายที่สามารถจำลองใน C ++ 03 ในระดับที่เพียงพอสำหรับการใช้งานจริง - และไม่มีความยุ่งยากทั้งหมดที่มาพร้อมกับเช่น: Boost Heck แม้แต่ข้อเสนอมาตรฐาน C ++ สำหรับnullptrแนะนำ backport ของ C ++ 03 และมี TR1 เป็นตัวอย่างสำหรับทุกอย่าง C ++ 11 ‑ แต่ ‑ เรา ‑ มี ‑ ตัวอย่าง ‑ สำหรับ stuff ปี ไม่เพียงแค่นั้นคุณสมบัติC ++ 14บางอย่างเช่นชุดการยืนยันชุดฟังก์ชันโปร่งใสและoptional สามารถใช้งานใน C ++ 03!

สองสิ่งเดียวที่ฉันรู้ว่าไม่สามารถย้อนกลับได้อย่างแน่นอนคือแม่แบบ constexpr และ variadic

เมื่อพิจารณาถึงเรื่องทั้งหมดของการเพิ่มรายการลงในเนมสเปซstdมุมมองของฉันคือว่ามันไม่สำคัญเลย คิดถึง Boost ซึ่งเป็นหนึ่งในห้องสมุด C ++ ที่สำคัญและเกี่ยวข้องที่สุดและการใช้งาน TR1: Boost.Tr1 หากคุณต้องการปรับปรุง C ++ ให้เข้ากันได้กับ C ++ 11 จากนั้นตามคำนิยามคุณจะเปลี่ยนเป็นสิ่งที่ไม่ใช่ C ++ 03 ดังนั้นการบล็อกตัวเองเหนือมาตรฐานที่คุณตั้งใจจะหลีกเลี่ยง ใส่ง่ายต่อต้าน คนพิถีพิถันจะบ่น แต่โดยนิยามแล้วเราไม่จำเป็นต้องสนใจพวกเขา

แน่นอนเพียงเพราะคุณจะไม่ได้ดังต่อไปนี้ (03) มาตรฐานหลังจากทั้งหมดไม่ได้หมายความว่าคุณจะไม่สามารถลองหรือจะไปสำราญไปรอบ ๆ ทำลายมัน นั่นไม่ใช่ประเด็น. ตราบใดที่คุณควบคุมอย่างระมัดระวังเกี่ยวกับสิ่งที่เพิ่มไว้ในstdเนมสเปซและมีการควบคุมสภาพแวดล้อมที่ใช้ซอฟต์แวร์ของคุณ (เช่น: ทำการทดสอบ!) ไม่ควรมีอันตรายใด ๆ หากเป็นไปได้ให้กำหนดทุกอย่างในเนมสเปซที่แยกต่างหากและเพิ่มเฉพาะusingคำสั่งให้กับเนมสเปซstdเพื่อที่คุณจะไม่เพิ่มสิ่งใดนอกเหนือไปจากสิ่งที่ "แน่นอน" จำเป็นต้องเข้าไป


Update (2013) : ตามคำขอของคำถามเดิมและเห็นความคิดเห็นบางส่วนที่ฉันไม่สามารถเพิ่มได้เนื่องจากการขาดตัวแทนนี่คือรายการของคุณสมบัติ C ++ 11 และ C ++ 14 และระดับการพกพา ถึง C ++ 03:

  • nullptr: อย่างเต็มที่ implementable ให้ย้ายกลับอย่างเป็นทางการของคณะกรรมการ; คุณอาจจะต้องมีความเชี่ยวชาญเฉพาะด้าน type_traits เพื่อให้เป็นที่รู้จักในฐานะ "native"
  • forward_list: สามารถนำไปใช้งานได้อย่างสมบูรณ์แม้ว่าการสนับสนุนตัวจัดสรรจะขึ้นอยู่กับการฝังตัวของ Tr1 ของคุณ
  • อัลกอริธึมใหม่ (partition_copy ฯลฯ ): สามารถนำไปใช้ได้อย่างสมบูรณ์
  • การสร้างตู้คอนเทนเนอร์จากวงเล็บปีกกา (เช่น:) vector<int> v = {1, 2, 3, 4};: สามารถนำไปใช้งานได้อย่างสมบูรณ์แม้ว่า wordier จะมากกว่าที่ต้องการ
  • static_assert: สามารถนำไปใช้งานได้เกือบทั้งหมดเมื่อนำมาใช้เป็นมาโคร (คุณจะต้องระมัดระวังด้วยเครื่องหมายจุลภาค)
  • unique_ptr: ใช้งานได้เกือบจะสมบูรณ์ แต่คุณจะต้องได้รับการสนับสนุนจากการเรียกรหัส (สำหรับการจัดเก็บในคอนเทนเนอร์ ฯลฯ ); ดูด้านล่างว่า
  • การอ้างอิง rvalue: สามารถนำไปใช้งานได้อย่างใกล้ชิดขึ้นอยู่กับจำนวนที่คุณคาดหวังที่จะได้รับจากพวกเขา (เช่น: Boost Move)
  • การทำซ้ำ Foreach: เกือบจะนำมาใช้อย่างเต็มที่ไวยากรณ์จะแตกต่างกันบ้าง
  • ใช้ฟังก์ชั่นท้องถิ่นเป็นข้อโต้แย้ง (เช่น.. แปลง): ใช้งานได้เกือบเต็มรูปแบบ แต่ไวยากรณ์จะแตกต่างกันพอตัวอย่างเช่นฟังก์ชั่นท้องถิ่นไม่ได้กำหนดไว้ที่เว็บไซต์โทร แต่ก่อนหน้านี้
  • ตัวดำเนินการแปลงที่ชัดเจน: สามารถนำไปใช้ในระดับปฏิบัติได้ (ทำให้เกิดการแปลงอย่างชัดเจน) ดู " C_ID_Crypt " ของC ++ ที่ไม่สมบูรณ์ แต่การรวมเข้ากับคุณสมบัติภาษาเช่นstatic_cast<>อาจเป็นไปไม่ได้
  • การส่งต่ออาร์กิวเมนต์: นำไปใช้กับระดับการปฏิบัติที่ระบุไว้ข้างต้นเกี่ยวกับการอ้างอิง rvalue แต่คุณจะต้องให้การโอเวอร์โหลด N ฟังก์ชั่นของคุณโดยรับอาร์กิวเมนต์ที่สามารถส่งต่อได้
  • ย้าย: นำไปใช้กับระดับการปฏิบัติ (ดูสอง aboves) แน่นอนคุณจะต้องใช้คอนเทนเนอร์ตัวดัดแปลงและวัตถุเพื่อทำกำไรจากสิ่งนี้
  • ตัวจัดสรรที่กำหนดขอบเขต: ไม่สามารถนำไปใช้งานได้จริงเว้นแต่ว่าการใช้งาน Tr1 ของคุณสามารถช่วยเหลือได้
  • ประเภทอักขระหลายไบต์: ไม่สามารถนำไปใช้งานได้จริงเว้นแต่ Tr1 ของคุณสามารถรองรับคุณได้ แต่สำหรับวัตถุประสงค์ที่ตั้งใจไว้จะดีกว่าที่จะใช้ไลบรารีที่ออกแบบมาเพื่อจัดการกับปัญหาเช่น ICU แม้ว่าจะใช้ C ++ 11 ก็ตาม
  • รายการอาร์กิวเมนต์ Variadic: นำไปใช้กับความยุ่งยากได้บ้าง
  • noexcept: ขึ้นอยู่กับคุณสมบัติของคอมไพเลอร์ของคุณ
  • ใหม่autoความหมายและdecltype: ขึ้นอยู่กับคุณสมบัติคอมไพเลอร์ของคุณ - __typeof__เช่น
  • ประเภทจำนวนเต็มขนาด ( int16_t, ฯลฯ ): ขึ้นอยู่กับคุณสมบัติของคอมไพเลอร์ของคุณ - หรือคุณสามารถมอบหมายให้ Portable stdint.h
  • คุณสมบัติประเภท: ขึ้นอยู่กับคุณสมบัติของคอมไพเลอร์ของคุณ
  • รายการเริ่มต้น: ไม่สามารถนำไปใช้กับความรู้ของฉันได้ อย่างไรก็ตามหากสิ่งที่คุณต้องการคือการเริ่มต้นภาชนะที่มีลำดับดูข้างต้นใน "การสร้างภาชนะ"
  • การสร้างชื่อแทนเทมเพลต: ไม่สามารถนำความรู้ของฉันไปใช้ได้ แต่มันเป็นคุณสมบัติที่ไม่จำเป็นและเรามี::typeในแม่แบบตลอดไป
  • เทมเพลต Variadic: ไม่สามารถนำไปใช้กับความรู้ของฉันได้ ปิดคือการเริ่มต้นอาร์กิวเมนต์แม่แบบซึ่งต้องมีความเชี่ยวชาญ N ฯลฯ
  • constexpr: ไม่สามารถนำไปใช้กับความรู้ของฉัน
  • การกำหนดค่าเริ่มต้นที่สม่ำเสมอ: ไม่สามารถนำไปใช้กับความรู้ของฉันได้ แต่การรับประกันการเริ่มต้น - ตัวสร้างเริ่มต้นสามารถนำมาใช้เป็นค่าเริ่มต้นของ Ala Boost ได้
  • C ++ 14 dynarray: สามารถนำไปใช้ได้อย่างสมบูรณ์
  • C ++ 14 optional<>: สามารถนำไปใช้งานได้เกือบสมบูรณ์ตราบใดที่คอมไพเลอร์ C ++ 03 ของคุณรองรับการตั้งค่าการจัดตำแหน่ง
  • ฟังก์ชั่นโปร่งใส C ++ 14: นำไปใช้งานได้เกือบทั้งหมด แต่รหัสลูกค้าของคุณอาจจะต้องใช้อย่างชัดเจนเช่น: std::less<void>เพื่อให้มันใช้งานได้
  • C ++ 14 ชุดการยืนยันใหม่ (เช่นassure): สามารถนำไปใช้ได้อย่างสมบูรณ์หากคุณต้องการการยืนยันและสามารถนำไปปฏิบัติได้อย่างเต็มที่หากคุณต้องการเปิดใช้งานการโยน
  • ส่วนขยาย tuple C ++ 14 (รับองค์ประกอบ tuple ตามประเภท): สามารถนำไปใช้งานได้อย่างสมบูรณ์และคุณสามารถทำให้ล้มเหลวในการคอมไพล์ด้วยเคสที่แน่นอนที่อธิบายไว้ในข้อเสนอคุณสมบัติ

(ข้อจำกัดความรับผิดชอบ: คุณสมบัติหลายอย่างเหล่านี้ถูกนำไปใช้ในไลบรารี backport C ++ ของฉันที่ฉันได้เชื่อมโยงข้างต้นดังนั้นฉันคิดว่าฉันรู้ว่าฉันกำลังพูดถึงอะไรเมื่อฉันพูดว่า "เต็ม" หรือ "ใกล้เต็ม")


6

นี่เป็นไปไม่ได้โดยพื้นฐาน std::unique_ptr<Thing>พิจารณา หากเป็นไปได้ที่จะเลียนแบบการอ้างอิงค่าเป็นห้องสมุดมันจะไม่เป็นคุณสมบัติภาษา


1
ฉันพูดว่า "ไม่ว่าจะอยู่ในระดับใดก็ตาม" เห็นได้ชัดว่าคุณสมบัติบางอย่างจะต้องถูกทิ้งไว้ข้างหลัง # ifdef หรือไม่ได้ใช้เลย std :: move () เกิดขึ้นเป็นหนึ่งที่คุณสามารถรองรับไวยากรณ์ (แม้ว่าไม่ใช่ฟังก์ชัน)
mcmcc

2
ที่จริงแล้วข้อเสนอการอ้างอิงค่า rvalueกล่าวถึงวิธีการใช้ห้องสมุด
Jan Hudec

โดยเฉพาะอย่างยิ่งมีความเป็นไปได้ที่จะใช้ความหมายของการย้ายสำหรับคลาสเฉพาะใน C ++ 03 ดังนั้นจึงควรเป็นไปได้ที่จะกำหนดที่std::unique_ptrนั่น แต่มีคุณสมบัติอื่น ๆ ของการอ้างอิง rvalue ไม่สามารถนำมาใช้ใน C ++ 03 std::forwardได้ อีกอย่างคือการที่std::unique_ptrจะไม่เป็นประโยชน์เพราะคอลเลกชันจะไม่ใช้ซีแมนทิกส์การย้ายเว้นแต่คุณจะแทนที่พวกเขาทั้งหมด
Jan Hudec

@JanHudec: ไม่สามารถกำหนดunique_ptrได้ auto_ptrดูความล้มเหลวของ unique_ptrเป็นจริงตัวอย่างตำราเรียนของชั้นเรียนที่มีความหมายถูกเปิดใช้งานพื้นฐานโดยคุณสมบัติภาษา
DeadMG

@DeadMG: ไม่มันไม่ได้unique_ptrถูกเปิดใช้งานโดยคุณสมบัติภาษา มันจะไม่เป็นประโยชน์มากหากไม่มีคุณสมบัตินั้น เพราะหากไม่มีการส่งต่อที่สมบูรณ์แบบจะไม่สามารถใช้งานได้ในหลายกรณีและการส่งต่อที่สมบูรณ์แบบนั้นจำเป็นต้องใช้คุณลักษณะนั้น
Jan Hudec

2
  1. Gcc เริ่มแนะนำ C ++ 11 (ยังคงเป็น C ++ 0x ในเวลานั้น) ใน 4.3 ตารางนี้กล่าวว่ามีการอ้างอิงค่าและคุณสมบัติที่ใช้งานน้อยกว่าอยู่แล้ว (คุณต้องระบุ-std=c++0xตัวเลือกเพื่อเปิดใช้งาน)
  2. ส่วนเพิ่มเติมไปยังไลบรารีมาตรฐานใน C ++ 11 ได้ถูกกำหนดไว้แล้วใน TR1 และ GNU stdlibc ++ จัดเตรียมไว้ใน std :: tr1 namespace ดังนั้นเพียงแค่ใช้เงื่อนไขที่เหมาะสม
  3. Boost จะกำหนดฟังก์ชั่น TR1 ส่วนใหญ่และสามารถแทรกลงในเนมสเปซ TR1 ได้หากคุณไม่มี (แต่ VS2010 ทำและ gcc 4.3 ทำได้เช่นกันหากคุณใช้ GNU stdlibc ++)
  4. การใส่อะไรในstdเนมสเปซเป็น "พฤติกรรมที่ไม่ได้กำหนด" นั่นหมายความว่าข้อกำหนดไม่ได้บอกว่าจะเกิดอะไรขึ้น แต่ถ้าคุณรู้ว่าในบางแพลตฟอร์มไลบรารี่มาตรฐานจะไม่สามารถกำหนดอะไรได้เลย เพียงแค่คาดหวังว่าคุณจะต้องตรวจสอบแต่ละแพลตฟอร์มสิ่งที่คุณต้องการและสิ่งที่คุณสามารถกำหนด
  5. ข้อเสนอสำหรับการอ้างอิง rvalue, N1690กล่าวถึงวิธีการใช้ความหมายย้ายใน C ++ 03 unique_ptrที่สามารถนำมาใช้เพื่อทดแทน อย่างไรก็ตามมันจะไม่เป็นประโยชน์เกินไปเพราะอาศัยคอลเลกชันจริง ๆ แล้วใช้ความหมายย้ายและคน C ++ 03 จะไม่ชัดเจน

1
คุณถูกต้องเกี่ยวกับ GCC แต่น่าเสียดายที่ฉันยังต้องสนับสนุนคอมไพเลอร์อื่น ๆ ที่ไม่ใช่ (GCC) สัญลักษณ์แสดงหัวข้อ # 4 ของคุณคือหัวใจของคำถามที่ฉันถาม # 5 เป็นที่น่าสนใจ แต่ฉันไม่ต้องการสนับสนุนซีแมนทิกส์การย้าย (การเพิ่มประสิทธิภาพการคัดลอก) บนแพลตฟอร์มเก่าเหล่านี้ แต่แทนที่จะเป็น "std :: move ()" เป็นไวยากรณ์ที่คอมไพล์ได้
mcmcc
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.