Đạt được khả năng tương thích với C ++ 11


12

Tôi làm việc trên một ứng dụng phần mềm lớn phải chạy trên nhiều nền tảng. Một số nền tảng này hỗ trợ một số tính năng của C ++ 11 (ví dụ: MSVS 2010) và một số không hỗ trợ bất kỳ (ví dụ: GCC 4.3.x). Tôi hy vọng tình trạng này sẽ tiếp tục trong vài năm (dự đoán tốt nhất của tôi: 3-5 năm).

Do đó, tôi muốn thiết lập một giao diện tương thích sao cho mọi người có thể viết mã C ++ 11 vẫn sẽ biên dịch với các trình biên dịch cũ hơn với mức bảo trì tối thiểu. Nhìn chung, mục tiêu là tối thiểu hóa # ifdef một cách hợp lý nhất có thể trong khi vẫn kích hoạt các tính năng / cú pháp C ++ 11 cơ bản trên các nền tảng hỗ trợ chúng và cung cấp mô phỏng trên các nền tảng không có.

Hãy bắt đầu với std :: move (). Cách rõ ràng nhất để đạt được khả năng tương thích là đặt một cái gì đó như thế này vào một tệp tiêu đề chung:

#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)

Điều này cho phép mọi người viết những thứ như

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

... với sự trừng phạt. Nó làm những gì họ muốn trong C ++ 11 và nó làm tốt nhất có thể trong C ++ 03. Khi cuối cùng chúng ta bỏ trình biên dịch C ++ 03 cuối cùng, mã này có thể vẫn như cũ.

Tuy nhiên, theo tiêu chuẩn, việc đưa các biểu tượng mới vào stdkhông gian tên là bất hợp pháp . Đó là lý thuyết. Câu hỏi của tôi là: thực tế nói có bất kỳ tác hại nào khi làm điều này như một cách để đạt được khả năng tương thích về phía trước?


1
Boost đã cung cấp khá nhiều thứ này và đã có mã để sử dụng các tính năng mới khi / nơi có sẵn, do đó bạn có thể chỉ cần sử dụng những gì Boost cung cấp và được thực hiện với nó. Tất nhiên, có những hạn chế - hầu hết các tính năng mới đã được thêm vào cụ thể vì các giải pháp dựa trên thư viện không đầy đủ.
Jerry Coffin

vâng, tôi đang suy nghĩ cụ thể về những tính năng có thể được thực hiện ở cấp thư viện, chứ không phải những thay đổi cú pháp. Boost không thực sự giải quyết vấn đề tương thích (liền mạch). Trừ khi tôi thiếu một cái gì đó ...
mcmcc

Gcc 4.3 đã có sẵn rất nhiều tính năng của C ++ 11, các tham chiếu Rvalue có lẽ là quan trọng nhất.
Jan Hudec

@JanHudec: Bạn nói đúng. Ví dụ đáng thương. Trong mọi trường hợp, có những trình biên dịch khác chắc chắn không hỗ trợ cú pháp (ví dụ: bất kỳ phiên bản trình biên dịch C ++ nào của IBM mà chúng tôi có).
mcmcc

Câu trả lời:


9

Tôi đã làm việc rất tốt trong khi vẫn giữ mức độ tương thích về phía trước và ngược trong các chương trình C ++ của mình, cho đến khi cuối cùng tôi phải tạo ra một bộ công cụ thư viện từ đó , tôi đang chuẩn bị phát hành đã được phát hành. Nói chung, miễn là bạn chấp nhận rằng bạn sẽ không có tính tương thích "hoàn hảo" về mặt tương thích cả về tính năng (một số thứ không thể được mô phỏng chuyển tiếp) không theo cú pháp (có thể bạn sẽ phải sử dụng macro, không gian tên thay thế cho một số điều) sau đó bạn đã hoàn tất.

Có rất nhiều tính năng có thể được mô phỏng trong C ++ 03 ở mức đủ để sử dụng thực tế - và không có tất cả những rắc rối đi kèm, ví dụ: Boost. Heck, ngay cả đề xuất tiêu chuẩn C ++ cho nullptrđề xuất một backport C ++ 03. Và sau đó có TR1 ví dụ cho mọi thứ C ++ 11 nhưng ‑ chúng tôi đã có ‑ bản xem trước cho năm thứ. Không chỉ vậy, một số tính năng của C ++ 14 như các biến thể khẳng định, chức năng trong suốt và optional có thể được thực hiện trong C ++ 03!

Hai điều duy nhất mà tôi biết rằng không thể hoàn toàn được nhập vào là các mẫu constexpr và matrixdic.

Liên quan đến toàn bộ vấn đề thêm công cụ vào không gian tên std, quan điểm của tôi về vấn đề này là không thành vấn đề - tất cả. Hãy nghĩ về Boost, một trong những thư viện C ++ quan trọng và có liên quan nhất và việc triển khai TR1: Boost.Tr1 của họ. Nếu bạn muốn cải thiện C ++, hãy làm cho nó chuyển tiếp tương thích với C ++ 11, thì theo định nghĩa, bạn đang biến nó thành một thứ không phải là C ++ 03, vì vậy, hãy tự chặn mình theo một Tiêu chuẩn mà bạn dự định tránh hoặc bỏ lại phía sau , chỉ đơn giản là đặt, phản tác dụng. Những người theo chủ nghĩa thuần túy sẽ phàn nàn, nhưng theo định nghĩa, người ta không cần phải quan tâm đến họ.

Tất nhiên, chỉ vì bạn sẽ không tuân theo (03) Tiêu chuẩn sau tất cả không có nghĩa là bạn không thể cố gắng, hoặc sẽ vui vẻ đi xung quanh phá vỡ nó. Đó không phải là vấn đề. Miễn là bạn giữ quyền kiểm soát rất cẩn thận đối với những gì được thêm vào stdkhông gian tên và kiểm soát các môi trường nơi phần mềm của bạn được sử dụng (ví dụ: thực hiện kiểm tra!), Thì không nên có bất kỳ tác hại nào. Nếu có thể, hãy xác định mọi thứ trong một không gian tên riêng biệt và chỉ thêm các usingchỉ thị vào không gian tên stdđể bạn không thêm bất cứ thứ gì ngoài những gì "hoàn toàn" cần phải đi vào. Cái nào, IINM, ít nhiều là những gì Boost.TR1 làm.


Cập nhật (2013) : như yêu cầu của câu hỏi ban đầu và thấy một số ý kiến ​​mà tôi không thể thêm vào do thiếu đại diện, đây là danh sách các tính năng của C ++ 11 và C ++ 14 và mức độ di động của chúng đến C ++ 03:

  • nullptr: có thể thực hiện đầy đủ với backport của Ủy ban chính thức; có lẽ bạn cũng sẽ phải cung cấp một số chuyên môn type_traits để nó được công nhận là loại "bản địa".
  • forward_list: hoàn toàn có thể thực hiện được, mặc dù sự hỗ trợ của bộ cấp phát phụ thuộc vào những gì mà ứng dụng Tr1 của bạn có thể cung cấp.
  • Các thuật toán mới (phân vùng_copy, v.v.): hoàn toàn có thể thực hiện được.
  • Các cấu trúc container từ chuỗi nối đôi (ví dụ vector<int> v = {1, 2, 3, 4};:): hoàn toàn có thể thực hiện được, mặc dù nhiều hơn một cách mong muốn.
  • static_assert: gần như hoàn toàn có thể thực hiện khi được triển khai dưới dạng macro (bạn sẽ chỉ phải cẩn thận với dấu phẩy).
  • unique_ptr: gần như hoàn toàn có thể thực hiện được, nhưng bạn cũng sẽ cần hỗ trợ từ việc gọi mã (để lưu trữ chúng trong các thùng chứa, v.v.); xem bên dưới mặc dù.
  • tham chiếu rvalue: gần như hoàn toàn có thể thực hiện được tùy thuộc vào số tiền bạn mong đợi nhận được từ chúng (ví dụ: Boost Move).
  • Foreach lặp: gần hoàn toàn có thể thực hiện, cú pháp sẽ khác nhau một chút.
  • sử dụng các hàm cục bộ làm đối số (ví dụ: biến đổi): gần như hoàn toàn có thể thực hiện được, nhưng cú pháp sẽ đủ khác nhau - ví dụ: các hàm cục bộ không được xác định tại trang web cuộc gọi mà ngay trước đó.
  • Các toán tử chuyển đổi rõ ràng: có thể thực hiện đến các mức thực tế (làm cho việc chuyển đổi được thực hiện rõ ràng), xem phần "tường minh" không hoàn hảo của C ++ ; nhưng việc tích hợp với các tính năng ngôn ngữ như static_cast<>có thể là gần như không thể.
  • chuyển tiếp đối số: có thể thực hiện đến các mức thực tế được nêu ở trên về tham chiếu giá trị, nhưng bạn sẽ cần cung cấp N quá tải cho các hàm của mình để lấy các đối số có thể chuyển tiếp.
  • di chuyển: có thể thực hiện đến mức thực tế (xem hai aboves). Tất nhiên, bạn phải sử dụng các thùng chứa và đối tượng sửa đổi để kiếm lợi từ việc này.
  • Phân bổ phạm vi: Không thực sự có thể thực hiện được trừ khi triển khai Tr1 của bạn có thể hỗ trợ nó.
  • Các loại ký tự đa dòng: Không thực sự có thể thực hiện được trừ khi Tr1 của bạn có thể hỗ trợ bạn. Nhưng với mục đích đã định, tốt hơn là dựa vào một thư viện được thiết kế đặc biệt để giải quyết vấn đề, chẳng hạn như ICU, ngay cả khi sử dụng C ++ 11.
  • Danh sách đối số Variadic: có thể thực hiện với một số rắc rối, chú ý đến chuyển tiếp đối số.
  • noexcept: phụ thuộc vào các tính năng của trình biên dịch của bạn.
  • autoNgữ nghĩa mới và decltype: phụ thuộc vào các tính năng của trình biên dịch của bạn - ví dụ : __typeof__.
  • các loại số nguyên có kích thước ( int16_t, v.v.): tùy thuộc vào các tính năng của trình biên dịch của bạn - hoặc bạn có thể ủy quyền cho Portable stdint.h.
  • loại thuộc tính: phụ thuộc vào các tính năng của trình biên dịch của bạn.
  • Danh sách khởi tạo: Không thể thực hiện theo kiến ​​thức của tôi; tuy nhiên nếu điều bạn muốn là khởi tạo các container theo trình tự, hãy xem phần bên trên về "các công trình của container".
  • Bí danh mẫu: Không hiểu theo kiến ​​thức của tôi, nhưng dù sao đó cũng là một tính năng không cần thiết và chúng tôi đã có ::typetrong các mẫu mãi mãi
  • Mẫu Variadic: Không thể thực hiện theo kiến ​​thức của tôi; đóng là mặc định đối số mẫu, yêu cầu N chuyên môn, v.v.
  • constexpr: Không thể thực hiện theo kiến ​​thức của tôi.
  • Khởi tạo thống nhất: Không thể thực hiện theo kiến ​​thức của tôi, nhưng việc khởi tạo trình tạo mặc định được đảm bảo có thể được triển khai khởi tạo giá trị của ala Boost.
  • C ++ 14 dynarray: thực hiện đầy đủ .
  • C ++ 14 optional<>: gần như hoàn toàn có thể thực hiện được miễn là trình biên dịch C ++ 03 của bạn hỗ trợ các thiết lập căn chỉnh.
  • Chức năng trong suốt của C ++ 14: gần như hoàn toàn có thể thực hiện được, nhưng mã máy khách của bạn có thể sẽ phải sử dụng rõ ràng, ví dụ: std::less<void>để làm cho nó hoạt động.
  • Các biến thể khẳng định mới của C ++ 14 (chẳng hạn như assure): có thể thực hiện đầy đủ nếu bạn muốn xác nhận, gần như hoàn toàn có thể thực hiện nếu bạn muốn bật ném thay thế.
  • Các phần mở rộng tuple C ++ 14 (lấy phần tử tuple theo loại): hoàn toàn có thể thực hiện được và thậm chí bạn có thể khiến nó không thể biên dịch với các trường hợp chính xác được mô tả trong đề xuất tính năng.

.


6

Điều này về cơ bản là không thể. Hãy xem xét std::unique_ptr<Thing>. Nếu có thể mô phỏng các tham chiếu giá trị như một thư viện, thì đó không phải là một tính năng ngôn ngữ.


1
Tôi đã nói "với bất cứ mức độ nào có thể". Rõ ràng một số tính năng sẽ phải bị bỏ lại phía sau # ifdef hoặc hoàn toàn không được sử dụng. std :: move () là một trong những bạn có thể hỗ trợ cú pháp (mặc dù không phải là chức năng).
mcmcc

2
Trên thực tế, đề xuất tham chiếu giá trị đề cập đến một giải pháp dựa trên thư viện!
Jan Hudec

Cụ thể hơn, có thể triển khai ngữ nghĩa di chuyển cho lớp cụ thể trong C ++ 03, do đó có thể xác định std::unique_ptrở đó, nhưng một số tính năng khác của tham chiếu giá trị không thể được thực hiện trong C ++ 03, vì vậy std::forwardkhông thể thực hiện được. Một điều khác là std::unique_ptrsẽ không hữu ích, vì các bộ sưu tập sẽ không sử dụng ngữ nghĩa di chuyển trừ khi bạn thay thế tất cả.
Jan Hudec

@JanHudec: Không thể định nghĩa unique_ptr. Nhìn vào những thất bại của auto_ptr. unique_ptrthực tế là ví dụ trong sách giáo khoa của một lớp học có ngữ nghĩa được kích hoạt cơ bản bởi tính năng ngôn ngữ.
DeadMG

@DeadMG: Không, nó không unique_ptrđược kích hoạt cơ bản bởi tính năng ngôn ngữ. Nó sẽ không hữu ích nếu không có tính năng đó. bởi vì không có chuyển tiếp hoàn hảo, nó sẽ không thể sử dụng được trong nhiều trường hợp và chuyển tiếp hoàn hảo không yêu cầu tính năng đó.
Jan Hudec

2
  1. Gcc bắt đầu giới thiệu C ++ 11 (vẫn là C ++ 0x tại thời điểm đó) trong 4.3. Bảng này cho biết nó đã có các tham chiếu giá trị và một số tính năng ít được sử dụng khác (bạn phải chỉ định -std=c++0xtùy chọn để bật chúng).
  2. Nhiều bổ sung cho thư viện chuẩn trong C ++ 11 đã được xác định trong TR1 và GNU stdlibc ++ cung cấp chúng trong không gian tên std :: tr1. Vì vậy, chỉ cần làm điều kiện thích hợp sử dụng.
  3. Boost định nghĩa hầu hết các hàm TR1 và có thể đưa chúng vào không gian tên TR1 nếu bạn không có nó (nhưng VS2010 cũng vậy và gcc 4.3 cũng vậy nếu bạn sử dụng GNU stdlibc ++).
  4. Đặt bất cứ thứ gì vào stdkhông gian tên là một "hành vi không xác định". Điều đó có nghĩa là đặc điểm kỹ thuật không nói điều gì sẽ xảy ra. Nhưng nếu bạn biết rằng trên nền tảng cụ thể, thư viện tiêu chuẩn không định nghĩa một cái gì đó, chỉ cần tiếp tục và xác định nó. Chỉ cần mong đợi rằng bạn sẽ phải kiểm tra trên từng nền tảng những gì bạn cần và những gì bạn có thể xác định.
  5. Các đề xuất về tài liệu tham khảo rvalue, N1690 đề cập đến làm thế nào để thực hiện ngữ nghĩa di chuyển trong C ++ 03. Điều đó có thể được sử dụng để thay thế unique_ptr. Tuy nhiên, nó sẽ không quá hữu ích, bởi vì nó phụ thuộc vào các bộ sưu tập thực sự sử dụng ngữ nghĩa di chuyển và C ++ 03 rõ ràng là không.

1
Bạn đã đúng về GCC nhưng thật không may, tôi cũng phải hỗ trợ các trình biên dịch khác (không phải GCC). Viên đạn số 4 của bạn là trung tâm của câu hỏi tôi đang hỏi. # 5 rất thú vị nhưng tôi không tìm cách hỗ trợ ngữ nghĩa di chuyển (tối ưu hóa bản sao) trên các nền tảng cũ này mà chỉ là "std :: move ()" như một cú pháp có thể biên dịch được.
mcmcc
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.