Досягнення сумісності вперед з 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, цей код може залишитися таким, як є.

Однак, згідно стандарту, вводити нові символи в область stdімен незаконно . Ось така теорія. Моє запитання: чи практично кажучи, чи є шкода робити це як спосіб досягнення сумісності вперед?


1
Boost вже передбачає небагато цього, і вже має код для використання нових функцій, коли / де це можливо, тому ви можете просто використовувати те, що надає Boost, і робити це з ним. Звичайно, є обмеження - більшість нових функцій були додані саме тому, що бібліотечні рішення не є адекватними.
Джеррі Труну

так, я думаю саме про ті особливості, які можна реалізувати на рівні бібліотеки, а не синтаксичні зміни. Boost насправді не вирішує питання (безперебійної) сумісності вперед. Якщо я чогось не пропускаю ...
mcmcc

Gcc 4.3 вже має кілька функцій C ++ 11, а Rvalue-посилання, мабуть, є найбільш важливим.
Ян Худек

@JanHudec: Ти маєш рацію. Поганий приклад. У будь-якому випадку, є інші компілятори, які точно не підтримують синтаксис (наприклад, будь-яку версію компілятора C ++ IBM у нас немає).
mcmcc

Відповіді:


9

Я працював добре, підтримуючи рівень сумісності вперед та назад у своїх програмах на C ++, поки в кінцевому підсумку мені не довелося зробити з нього бібліотечний інструментарій , який я готую до випуску , вже вийшов. Загалом, доки ви приймете, що ви не отримаєте "ідеальної" сумісності форвардів ні в функціях (деякі речі просто неможливо перепрограмувати вперед), ні в синтаксисі (можливо, вам доведеться використовувати макроси, альтернативні простори імен для деякі речі) то ви все налаштовані.

Існує велика кількість функцій, які можна наслідувати в C ++ 03 на рівні, достатньому для практичного використання - і без усіх клопотів, які виникають, наприклад: Boost. Чорт забирає, навіть пропозиція стандартів C ++ nullptrпропонує підказку C ++ 03. А потім є TR1, наприклад, для всього C ++ 11, але - у нас були попередні перегляди - протягом років. Мало того, деякі функції C ++ 14, такі як варіанти затвердження, прозорі функтори, і optional можуть бути реалізовані в C ++ 03!

Єдині дві речі, які я знаю, що їх неможливо повністю підтримати - це шаблони конвекстра та варіативні.

Що стосується всього питання додавання матеріалів до простору імен std, я вважаю, що це зовсім не має значення . Подумайте про Boost, одну з найважливіших і найбільш релевантних бібліотек C ++ та їх реалізацію TR1: Boost.Tr1. Якщо ви хочете вдосконалити C ++, зробіть його вперед сумісним із C ++ 11, то, за визначенням, ви перетворюєте його на щось, що не є C ++ 03, тому блокування себе за Стандартом, якого ви хочете уникнути або залишити позаду так чи інакше , просто кажучи, контрпродуктивний. Пуристи будуть скаржитися, але за визначенням їх не потрібно дбати.

Звичайно, саме тому, що ви не будете дотримуватися стандарту (03), це ще не означає, що ви не можете намагатися, або будете радісно обіймати його. Це не суть. Поки ви будете дуже ретельно контролювати те, що додано в stdпростір імен, і контролювати середовища, де використовується ваше програмне забезпечення (тобто: робіть тестування!), Взагалі не повинно бути ніякої непереборної шкоди. Якщо можливо, визначте все в окремому просторі імен і додайте лише usingдирективи до простору імен, stdщоб ви нічого не додавали там, крім того, що потрібно "абсолютно". Що, IINM, є більш-менш тим, що робить Boost.TR1.


Оновлення (2013) : як запит на оригінальне запитання та побачивши деякі коментарі, до яких я не можу додати через відсутність повторень, ось список функцій C ++ 11 та C ++ 14 та ступінь їх переносимості до C ++ 03:

  • nullptr: повністю реалізована з огляду на офіційний Комітет; Вам, мабуть, доведеться також надати деякі спеціалізації type_traits, щоб вони були визнані "рідним" типом.
  • forward_list: повністю реалізована, хоча підтримка розподільника покладається на те, що може надати ваше ім'я Tr1.
  • Нові алгоритми (partition_copy тощо): повністю реалізовані.
  • Конструкції контейнерів з дужок-послідовностей (напр . :) vector<int> v = {1, 2, 3, 4};: повністю реалізовані, хоч і важчіші, ніж хотілося б.
  • static_assert: майже повністю реалізований, коли реалізований як макрос (обережним буде лише комами).
  • unique_ptr: майже повністю реалізований, але вам також потрібна підтримка коду виклику (для зберігання їх у контейнерах тощо); див. нижче, хоча.
  • rvalue-reference: майже повністю реалізовані залежно від того, скільки ви очікуєте отримати від них (наприклад: Boost Move).
  • Попереджуйте ітерацію: майже повністю реалізований синтаксис дещо відрізнятиметься.
  • використання локальних функцій в якості аргументів (наприклад: перетворення): майже повністю реалізований, але синтаксис буде досить різним - наприклад, локальні функції не визначені на сайті виклику, а прямо раніше.
  • явні оператори перетворення: реалізовані на практичних рівнях (отримання конверсії робиться явною), див. " Явний_випуск" + " Недосконалий C ++ "; але інтеграція з мовними функціями, такими як, static_cast<>можливо, майже неможлива.
  • переадресація аргументів: можлива реалізація на практичних рівнях, з огляду на вищезазначене на rvalue-посиланнях, але вам потрібно буде забезпечити N перевантажень для ваших функцій, приймаючи аргументи, що не підлягають зміні.
  • переміщення: реалізовується на практичних рівнях (див. дві підказки). Звичайно, вам доведеться використовувати контейнери-модифікатори та об'єкти, щоб отримати з цього прибуток.
  • Розподілені алокатори: Не реально реалізується, якщо ваша реалізація Tr1 може допомогти в цьому.
  • багатобайтові типи символів: Не реально реалізовано, якщо ваш Tr1 може підтримати вас. Але за призначенням краще покластися на бібліотеку, спеціально розроблену для вирішення цього питання, наприклад, ICU, навіть якщо використовується C ++ 11.
  • Переліки різноманітних аргументів: реалізовані з певними клопотами, зверніть увагу на переадресацію аргументів.
  • noexcept: залежить від особливостей вашого компілятора.
  • Нова autoсемантика і decltype: залежить від особливостей вашого компілятора - напр .: __typeof__.
  • цілі типи розміру ( int16_tтощо): залежать від особливостей вашого компілятора - або ви можете делегувати портативний stdint.h.
  • атрибути типу: залежать від особливостей вашого компілятора.
  • Список ініціалізаторів: Наскільки мені невідомо; однак якщо ви хочете ініціалізувати контейнери з послідовностями, дивіться вище про "конструкції контейнерів".
  • Надання псевдоніму шаблонів: Наскільки мені не відомо, але це все-таки непотрібна функція, і ми маємо ::typeв шаблонах назавжди
  • Варіантні шаблони: Наскільки мені невідомо; тісно - аргумент шаблону за замовчуванням, який вимагає N спеціалізацій тощо.
  • constexpr: Наскільки мені невідомо.
  • Уніфікована ініціалізація: наскільки мені не відомо, але гарантована за замовчуванням ініціалізація конструктора може бути реалізована ala Boost, ініціалізована значенням.
  • C ++ 14 dynarray: повністю реалізований.
  • C ++ 14 optional<>: майже повністю реалізований, якщо ваш компілятор C ++ 03 підтримує налаштування вирівнювання.
  • C ++ 14 прозорих функторів: майже повністю реалізований, але ваш клієнтський код, ймовірно, повинен явно використовувати, наприклад: std::less<void>для того, щоб він працював.
  • C ++ 14 нових варіантів затвердження (таких як assure): повністю реалізований, якщо ви хочете стверджувати, майже повністю реалізований, якщо ви хочете замість цього включити кидки.
  • C ++ 14 розширень кортежу (отримати елемент кортежу за типом): повністю реалізований, і ви навіть можете змусити його не виконати компіляцію з точними випадками, описаними в пропозиції про функції.

(Відмова від відповідальності: кілька цих функцій реалізовані в моїй бібліотеці C ++, яку я зв'язав вище, тому я думаю, що я знаю, про що я говорю, коли кажу "повністю" або "майже повністю".)


6

Це принципово неможливо. Розглянемо std::unique_ptr<Thing>. Якби можна було імітувати посилання rvalue як бібліотеку, це не було б мовною особливістю.


1
Я сказав "наскільки це можливо". Очевидно, що деякі функції доведеться залишати позаду # ifdef або взагалі не використовувати. std :: move () трапляється таким, що ви можете підтримувати синтаксис (хоч і не функціональність).
mcmcc

2
Насправді пропозиція релевантних посилань згадує рішення про бібліотеку!
Ян Худек

Більш конкретно, можна реалізувати семантику переміщення для конкретного класу в C ++ 03, тому там слід мати можливість визначити std::unique_ptr, але деякі інші особливості посилань на rvalue не можуть бути реалізовані в C ++ 03, тому std::forwardце неможливо. Інша справа, що це std::unique_ptrне буде корисним, оскільки колекції не використовуватимуть семантику переміщення, якщо ви не заміните їх усі.
Ян Худек

@JanHudec: Неможливо визначити unique_ptr. Подивіться на недоліки auto_ptr. unique_ptrє практично підручником приклад класу, семантика якого принципово була включена мовною особливістю.
DeadMG

@DeadMG: Ні, це не unique_ptrпринципово включено мовною функцією. Але без цієї функції це було б не дуже корисно. тому що без ідеального переадресації це не було б корисним у багатьох випадках, а для ідеального переадресації потрібна ця функція.
Ян Худек

2
  1. Gcc почав вводити C ++ 11 (ще C ++ 0x на той час) у 4.3. У цій таблиці йдеться про те, що в ньому вже є посилання на rvalue та деякі інші менш використовувані функції (вам потрібно вказати -std=c++0xопцію, щоб їх включити).
  2. Багато доповнень до стандартної бібліотеки на C ++ 11 були вже визначені в TR1, і GNU stdlibc ++ надає їх у просторі імен std :: tr1. Тому просто виконайте відповідне умовне використання.
  3. Boost визначає більшість функцій TR1 і може вводити їх у простір імен TR1, якщо у вас його немає (але VS2010 робить і gcc 4.3 добре, якщо ви використовуєте GNU stdlibc ++).
  4. Введення чого-небудь у stdпростір імен - це "невизначена поведінка". Це означає, що специфікація не говорить про те, що буде. Але якщо ви знаєте, що на певній платформі стандартна бібліотека щось не визначає, просто вперед і визначте це. Просто очікуйте, що вам доведеться перевірити на кожній платформі, що вам потрібно і що ви можете визначити.
  5. У пропозиції щодо посилань на оцінку N1690 вказується, як реалізувати семантику переміщення в C ++ 03. Це можна використати для заміни unique_ptr. Однак це було б не надто корисно, оскільки він покладається на колекції, які фактично використовують семантику переміщення, а C ++ 03 - очевидно, не будуть.

1
Ви вірно ставитесь до GCC, але, на жаль, я також повинен підтримувати інші (не GCC) компілятори. Ваша куля №4 лежить в основі питання, яке я задаю. №5 цікаво, але я не хочу підтримувати семантику переміщення (оптимізацію копіювання) на цих старих платформах, а просто "std :: move ()" як синтаксис, що компілюється.
mcmcc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.