C ++ 11 ile ileri uyumluluk elde etme


12

Birkaç platformda çalışması gereken büyük bir yazılım uygulaması üzerinde çalışıyorum. Bu platformlardan bazıları C ++ 11'in bazı özelliklerini (örn. MSVS 2010) destekler, bazıları ise desteklemez (örn. GCC 4.3.x). Bu durumun birkaç yıl devam etmesini bekliyorum (en iyi tahminim: 3-5 yıl).

Bu göz önüne alındığında, ben (mümkün olan her dereceye kadar) insanlar hala en az bakım ile eski derleyiciler ile derleyecek C ++ 11 kodu yazabilirsiniz böylece bir uyumluluk arayüzü kurmak istiyorum. Genel olarak, amaç # ifdef'leri makul ölçüde mümkün olduğunca en aza indirgemek ve aynı zamanda onları destekleyen platformlarda temel C ++ 11 sözdizimi / özelliklerini etkinleştirmek ve desteklemeyen platformlarda emülasyon sağlamaktır.

Std :: move () ile başlayalım. Uyumluluğu sağlamanın en belirgin yolu, ortak bir başlık dosyasına böyle bir şey koymak olacaktır:

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

Bu, insanların

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

... Dokunulmazlık ile birlikte. C ++ 11'de istediklerini yapar ve C ++ 03'te yapabileceği en iyisini yapar. Sonunda C ++ 03 derleyicilerinin sonuncusunu bıraktığımızda, bu kod olduğu gibi kalabilir.

Bununla birlikte, standarda göre, stdisim alanına yeni semboller enjekte etmek yasadışıdır . Teori bu. Benim sorum: pratik olarak , bunu ileri uyumluluğa ulaşmanın bir yolu olarak yapmanın herhangi bir zararı var mı?


1
Boost zaten bunun birazını sağlar ve mevcut olduğunda / mevcut olduğunda yeni özellikleri kullanmak için koda sahiptir, bu nedenle Boost'un sağladığı şeyleri kullanabilirsiniz ve onunla yapılabilir. Tabii ki, sınırlamalar var - çoğu yeni özellik özellikle kütüphane tabanlı çözümler yeterli olmadığı için eklendi.
Jerry Coffin

evet, özellikle sözdizimsel değişiklikleri değil, kütüphane düzeyinde uygulanabilecek özellikleri düşünüyorum. Boost, (kesintisiz) ileri uyumluluk sorununu gerçekten ele almaz. Bir şey eksik olmadıkça ...
mcmcc

Gcc 4.3 zaten bir avuç C ++ 11 özelliğine sahiptir, Rvalue referansları muhtemelen en önemlisidir.
Jan Hudec

@ JanHudec: Haklısın. Kötü örnek. Her durumda, sözdizimini kesinlikle desteklemeyen başka derleyiciler de vardır (örneğin, IBM'in C ++ derleyicisinin herhangi bir sürümü varsa).
mcmcc

Yanıtlar:


9

Benim C ++ programlarında türev araçlarının ve geriye doğru uyumluluk düzeyi tutmak ise bir iyiliği için çalışıyoruz Sonunda bunun dışında bir kütüphane araç yapmak zorunda dek , ben serbest bırakılması için hazırlık yapıyorum zaten serbest bırakıldı. Genel olarak, sözdiziminde olmayan özelliklerde (bazı şeyler ileriye doğru taklit edilemez) "mükemmel" ileri uyumluluğunu alamayacağınızı kabul ettiğiniz sürece (muhtemelen makroları, alternatif ad alanlarını kullanmanız gerekecektir) bazı şeyler) o zaman hepiniz hazırsınız.

C ++ 03'te pratik kullanım için yeterli bir seviyede emüle edilebilen iyi bir çok özellik var - ve örneğin ile gelen tüm güçlükler olmadan: Boost. Heck, hatta C ++ standart önerisi için nullptrbir C ++ 03 backport önerir. Ve sonra örneğin C ++ 11 everything her şey için TR1 var ama ‑ ‑ yıl stuff için ‑ önizleme yaptık ‑. Sadece, bazı o C ++ 14 assert gibi özellikler şeffaf fanktorlar varyantları ve optional edebilirsiniz C ++ 03 uygulanacak!

Kesinlikle desteklenemediğini bildiğim sadece iki şey, constexpr ve varyasyon şablonları.

Ad alanına bir şeyler ekleme konusuyla ilgili olarak std, benim görüşüm bunun hiç önemli olmadığıdır . En önemli ve ilgili C ++ kütüphanelerinden biri olan Boost'u ve bunların TR1: Boost.Tr1'i uygulamasını düşünün. C ++ 'ı geliştirmek istiyorsanız, C ++ 11 ile uyumlu hale getirin, o zaman tanımı gereği C ++ 03 olmayan bir şeye dönüştürüyorsunuz , bu yüzden kendinizi önlemek veya geride bırakmak istediğiniz bir Standart üzerinde kendinizi bloke etmek , basitçe söylemek gerekirse, verimsiz. Puristler şikayet edecek, ancak tanım gereği, onları önemsememelidir.

Tabii ki, sadece (03) Standardını takip etmeyeceğiniz için, denemeyeceğiniz anlamına gelmez ya da onu kırmak için neşeyle dolaşırsınız . Konu o değil. Ad alanına neyin eklendiğini çok dikkatli stdbir şekilde kontrol ettiğiniz ve yazılımınızın kullanıldığı ortamların kontrolüne sahip olduğunuz sürece (yani: test yapın!), Hiçbir şekilde kontrol edilemeyen bir zarar olmamalıdır. Mümkünse, her şeyi ayrı bir ad alanında tanımlayın ve yalnızca "kesinlikle" girilmesi gereken şeylerin ötesinde hiçbir şey eklememeniz usingiçin ad alanına yalnızca yönergeler ekleyin std.


Güncelleme (2013) : orijinal sorunun talebi ve tekrarlama eksikliğinden dolayı ekleyemediğim yorumların bazılarını görmek için, C ++ 11 ve C ++ 14 özelliklerinin bir listesi ve taşınabilirlik derecesi C ++ 03'e kadar:

  • nullptr: resmi Komite'nin backport'u göz önüne alındığında tamamen uygulanabilir; muhtemelen bir "yerel" tür olarak tanınması için bazı type_traits uzmanlıkları sağlamanız gerekir.
  • forward_list: Ayırıcı desteği Tr1 uygulamanızın neler sağlayabileceğine bağlı olsa da, tamamen uygulanabilir.
  • Yeni algoritmalar (partition_copy, vb.): Tamamen uygulanabilir.
  • Brace sekanslarından (örn .:) konteyner konstrüksiyonları vector<int> v = {1, 2, 3, 4};: istenenden daha kötü olsa da, tamamen uygulanabilir.
  • static_assert: bir makro olarak uygulandığında neredeyse tamamen uygulanabilir (yalnızca virgülle dikkatli olmanız gerekir).
  • unique_ptr: neredeyse tamamen uygulanabilir, ancak arama kodundan da destek almanız gerekir (bunları kaplarda saklamak için vb.); aşağıya bakın.
  • rvalue referansları: onlardan ne kadar bekleyeceğinize bağlı olarak neredeyse tamamen uygulanabilir (örn .: Boost Move).
  • Her bir yineleme: neredeyse tamamen uygulanabilir, sözdizimi biraz farklı olacaktır.
  • yerel işlevleri bağımsız değişken olarak kullanma (örneğin: transform): neredeyse tam olarak uygulanabilir, ancak sözdizimi yeterince farklılık gösterir - örneğin, yerel işlevler çağrı sitesinde tanımlanmaz, hemen önce tanımlanır.
  • açık dönüşüm işleçleri: pratik düzeylere uygulanabilir (dönüştürmenin açık hale getirilmesi), bkz. Imperfect C ++ 'ın "explicit_cast"; ancak dil özellikleriyle entegrasyon static_cast<>neredeyse imkansız olabilir.
  • argüman iletme: yukarıdaki rvalue referanslarında verilen pratik seviyelere uygulanabilir, ancak iletilebilir argümanlar alarak işlevlerinize N aşırı yük sağlamanız gerekir.
  • hareket: pratik seviyelere uygulanabilir (bkz. yukarıda). Elbette, bundan yararlanmak için değiştirici kapları ve nesneleri kullanmanız gerekir.
  • Kapsamlı paylaştırıcılar: Tr1 uygulamanız buna yardımcı olmadıkça gerçekten uygulanamaz.
  • çok baytlı karakter türleri: Tr1'iniz sizi destekleyemediği sürece gerçek anlamda uygulanamaz. Ancak amaçlanan amaç için, C ++ 11 kullanıyor olsa bile , ICU gibi konuyla ilgilenmek için özel olarak tasarlanmış bir kütüphaneye güvenmek daha iyidir .
  • Değişken argüman listeleri: bazı güçlüklerle uygulanabilir, argüman iletmeye dikkat edin.
  • noexcept: derleyicinizin özelliklerine bağlıdır.
  • Yeni autosemantik ve decltype: derleyicinizin özelliklerine bağlıdır - örn .: __typeof__.
  • boyutlu tamsayı türleri ( int16_tvb.): derleyicinizin özelliklerine bağlıdır - veya Portable stdint.h dosyasına delege edebilirsiniz.
  • type öznitelikleri: derleyicinizin özelliklerine bağlıdır.
  • Başlatıcı listesi: Bildiğim kadarıyla uygulanamaz; ancak kapsayıcıları sıralı olarak başlatmak istiyorsanız, "kapsayıcı konstrüksiyonlar" ile ilgili yukarıdakilere bakınız.
  • Şablon Takma Adı: Bildiğim kadarıyla uygulanamaz, ancak yine de gereksiz bir özellik ve ::typeşablonlarda sonsuza kadar yaşadık
  • Değişken şablonları: Bildiğim kadarıyla uygulanamaz; kapanış, N uzmanlığı vb. gerektiren şablon bağımsız değişkenidir.
  • constexpr: Bildiğim kadarıyla uygulanamaz.
  • Düzgün başlatma: Bilgilerime uygulanamaz, ancak garantili varsayılan yapıcı başlatma, ala Boost'un değer başlangıç ​​değeriyle uygulanabilir.
  • C ++ 14 dynarray: tamamen uygulanabilir.
  • C ++ 14 optional<>: C ++ 03 derleyiciniz hizalama kurulumlarını desteklediği sürece neredeyse tamamen uygulanabilir.
  • C ++ 14 şeffaf functors: neredeyse tamamen uygulanabilir, ancak istemci kodunuzun çalışması için büyük olasılıkla örneğin: kullanması gerekecektir std::less<void>.
  • C ++ 14 yeni assert (örneğin varyantları assure:) tamamen yerine atar sen etkinleştirmek istiyorsanız hemen tam olarak uygulanabilir, iddia istiyorsanız uygulanabilir.
  • C ++ 14 tuple uzantıları (tuple elemanını türe göre al): tamamen uygulanabilir ve hatta özellik teklifinde açıklanan tam durumlarla derlenemediğini bile alabilirsiniz.

(Feragatname: Bu özelliklerin birçoğu yukarıda bağladığım C ++ backports kütüphanesinde uygulandığından, "tamamen" veya "neredeyse tamamen" dediğimde neden bahsettiğimi biliyorum.)


6

Bu temel olarak imkansız. Düşünün std::unique_ptr<Thing>. Rvalue referanslarını bir kütüphane olarak taklit etmek mümkün olsaydı, bir dil özelliği olmazdı.


1
"Mümkün olan her dereceye kadar" dedim. Açıkçası bazı özelliklerin # ifdef'lerin geride bırakılması veya hiç kullanılmaması gerekecek. std :: move (), sözdizimini destekleyebileceğiniz bir işlevdir (işlevsellik olmasa da).
mcmcc

2
Aslında değer referansları önerisi kütüphane tabanlı bir çözümden bahsediyor!
Jan Hudec

Daha spesifik olarak, C ++ 03'te belirli bir sınıf için hareket semantiğini uygulamak mümkündür, bu yüzden orada tanımlamak mümkün olmalıdır std::unique_ptr, ancak C ++ 03'te rvalue referanslarının diğer bazı özellikleri uygulanamaz, bu yüzden std::forwardmümkün değildir. Diğer bir şey, std::unique_ptrişe yaramayacağıdır, çünkü koleksiyonlar hepsini değiştirmedikçe taşıma semantiğini kullanmayacaktır.
Jan Hudec

@ JanHudec: Tanımlamak mümkün değil unique_ptr. Başarısızlıklarına bak auto_ptr. unique_ptrpratikte ise ders kitabı semantik temelde dil özelliğinin etkin olduğu bir sınıfın örneği.
DeadMG

@DeadMG: Hayır, unique_ptrtemel olarak dil özelliği tarafından etkinleştirilmiş değil . Yine de bu özellik olmadan çok yararlı olmazdı. çünkü mükemmel yönlendirme olmadan birçok durumda kullanılamaz ve kusursuz yönlendirme bu özelliği gerektirir.
Jan Hudec

2
  1. Gcc, 4.3'te C ++ 11'i (o sırada hala C ++ 0x) tanıtmaya başladı. Bu tablo zaten rvalue referanslarına ve daha az kullanılan bazı özelliklere sahip olduğunu söylüyor ( -std=c++0xbunları etkinleştirme seçeneğini belirtmeniz gerekiyor).
  2. C ++ 11'de standart kütüphaneye birçok ekleme zaten TR1'de tanımlanmış ve GNU stdlibc ++ bunları std :: tr1 ad alanında sağlar. Bu yüzden uygun koşullu kullanımı yapın.
  3. Boost, TR1 işlevlerinin çoğunu tanımlar ve eğer yoksa TR1 ad alanına enjekte edebilir (ancak GNU stdlibc ++ kullanıyorsanız VS2010 yapar ve gcc 4.3 de yapar).
  4. Ad alanına herhangi bir şey koymak std"tanımsız bir davranış" tır. Bu, şartnamenin ne olacağını söylemediği anlamına gelir. Ancak, belirli bir platformda standart kütüphanenin bir şey tanımlamadığını biliyorsanız, devam edin ve tanımlayın. Her bir platformda neye ihtiyacınız olduğunu ve neyi tanımlayabileceğinizi kontrol etmeniz gerekecek.
  5. Rvalue referanslar önerisi, N1690 03 C ++ hareket anlambilim nasıl uygulanacağını bahseder. Bu ikame etmek için kullanılabilir unique_ptr. Ancak, çok yararlı olmaz, çünkü aslında hareket semantiğini kullanan koleksiyonlara dayanır ve C ++ 03 olanlar açıkça görülmez.

1
GCC konusunda haklısınız, ancak maalesef diğer (GCC olmayan) derleyicileri de desteklemem gerekiyor. 4 numaralı mermin, sorduğum sorunun merkezinde. # 5 ilginç ama bu eski platformlarda taşıma semantiğini (kopya optimizasyonu) desteklemiyorum, daha çok derlenebilir bir sözdizimi olarak "std :: move ()" işlevini destekliyorum.
mcmcc
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.