C ++ 17, C ++ 14 ve C ++ 11 nesnelerini bağlamak güvenli midir?


103

Diyelim ki hepsi aynı derleyici / sürüm tarafından üretilmiş üç derlenmiş nesnem var :

  1. A, C ++ 11 standardı ile derlendi
  2. B, C ++ 14 standardıyla derlendi
  3. C, C ++ 17 standardıyla derlenmiştir

Basit olması için, tüm üstbilgilerin yalnızca üç standart sürüm arasında anlamsallığı değişmeyen yapılar kullanılarak C ++ 11'de yazıldığını ve bu nedenle herhangi bir karşılıklı bağımlılığın başlık dahil etme ile doğru bir şekilde ifade edildiğini ve derleyicinin buna itiraz etmediğini varsayalım .

Bu nesnelerin hangi kombinasyonlarıdır ve tek bir ikiliye bağlanmak güvenli değil mi? Neden?


DÜZENLEME: büyük derleyicileri kapsayan yanıtlar (örneğin gcc, clang, vs ++) açıktır


6
Okul / mülakat sorusu değil. Soru belirli bir durumdan kaynaklanıyor: Açık kaynaklı bir kütüphaneye bağlı bir proje üzerinde çalışıyorum. Bu kitaplığı kaynaktan oluşturuyorum, ancak onun derleme sistemi yalnızca C ++ 03 / C ++ 11 binası arasında seçim yapmak için bir bayrak kabul ediyor. Kullandığım derleyici diğer standartları da destekliyor ve kendi projemi C ++ 17'ye yükseltmeyi düşünüyorum. Bunun güvenli bir karar olup olmadığından emin değilim. ABI'da bir kırılma veya yaklaşımın tavsiye edilemeyeceği başka bir yol olabilir mi? Net bir cevap bulamadım ve genel durumla ilgili bir soru göndermeye karar verdim.
ricab

6
Bu tamamen derleyiciye bağlıdır. Resmi C ++ spesifikasyonlarında bu durumu yöneten hiçbir şey yoktur. C ++ 03 veya C + 11 standartlarına yazılan kodun C ++ 14 ve C ++ 17 düzeyinde bazı sorunlara sahip olma ihtimali de çok azdır. Yeterli bilgi ve deneyimle (ve başlamak için iyi yazılmış kodla), bu sorunlardan herhangi birinin düzeltilmesi mümkün olmalıdır. Bununla birlikte, daha yeni C ++ standartlarına çok aşina değilseniz, derleme sisteminin desteklediği ve çalışmak için test edilen şeye bağlı kalmanız daha iyidir.
Sam Varshavchik

10
@Someprogrammerdude: Bu son derece değerli bir soru. Keşke bir cevabım olsaydı. Tek bildiğim, RHEL devtoolset aracılığıyla libstdc ++ 'nın, yeni öğelere statik olarak bağlanarak ve eski öğeleri dağıtımın "yerel" libstdc ++' yı kullanarak çalışma zamanında dinamik olarak çözümlemek üzere bırakarak, tasarım gereği geriye dönük olarak uyumlu olduğudur. Ama bu sorunun cevabı değil.
Yörüngede Hafiflik Yarışları

4
@nm: ... çoğunlukla durum böyledir ... dağıtımdan bağımsız C ++ kitaplıklarını dağıtan hemen hemen herkes bunu (1) dinamik kitaplık biçiminde ve (2) arabirim sınırlarında C ++ standart kitaplık kapsayıcıları olmadan yapar. Linux dağıtımından gelen kitaplıklar, hepsi aynı derleyici, aynı standart kitaplık ve hemen hemen aynı varsayılan bayrak kümesiyle oluşturuldukları için bunu kolaylaştırır.
Matteo Italia

3
@ MatteoItalia'dan önceki yorumu netleştirmek için "ve C ++ 03'ten C ++ 11 moduna geçerken (özellikle std :: string)." Bu doğru değildir, std::stringlibstdc ++ 'daki aktif uygulama kullanılan moddan bağımsızdır-std . Bu önemli bir özelliktir tam OP adlı gibi destek durumlara. Yeniyi std::stringC ++ 03 kodunda kullanabilir ve eskisini std::stringC ++ 11 kodunda kullanabilirsiniz (Matteo'nun sonraki yorumundaki bağlantıya bakın).
Jonathan Wakely

Yanıtlar:


123

Bu nesnelerin hangi kombinasyonlarıdır ve tek bir ikiliye bağlanmak güvenli değil mi? Neden?

GCC için, A, B ve C nesnelerinin herhangi bir kombinasyonunu birbirine bağlamak güvenlidir. Eğer hepsi aynı sürümle oluşturulmuşsa, ABI uyumludur, standart sürüm (yani -stdseçenek) herhangi bir fark yaratmaz.

Neden? Çünkü bu, sağlamak için çok çalıştığımız uygulamamızın önemli bir özelliği.

Sorun yaşadığınız yer, GCC'nin farklı sürümleriyle derlenmiş nesneleri birbirine bağlarsanız ve GCC'nin bu standarda yönelik desteği tamamlanmadan önce yeni bir C ++ standardından gelen kararsız özellikleri kullandıysanız. Örneğin, GCC 4.9 kullanarak -std=c++11bir nesneyi ve GCC 5 ile başka bir nesneyi derlerseniz -std=c++11sorun yaşarsınız. C ++ 11 desteği, GCC 4.x'te deneyseldi ve bu nedenle, C ++ 11 özelliklerinin GCC 4.9 ve 5 sürümleri arasında uyumsuz değişiklikler vardı. Benzer şekilde, bir nesneyi GCC 7 ile -std=c++17ve başka bir nesneyi GCC 8 ile derlerseniz ve -std=c++17sorun yaşarsınız çünkü GCC 7 ve 8'de C ++ 17 desteği hala deneysel ve gelişmektedir.

Öte yandan, aşağıdaki nesnelerin herhangi bir kombinasyonu çalışacaktır (ancak libstdc++.sosürümle ilgili aşağıdaki nota bakın ):

  • GCC 4.9 ile derlenen D nesnesi ve -std=c++03
  • GCC 5 ile derlenmiş E nesnesi ve -std=c++11
  • GCC 7 ile derlenen F nesnesi ve -std=c++17

Bunun nedeni, C ++ 03 desteğinin kullanılan üç derleyici sürümünde de kararlı olmasıdır ve bu nedenle C ++ 03 bileşenleri tüm nesneler arasında uyumludur. C ++ 11 desteği GCC 5'ten beri kararlıdır, ancak D nesnesi herhangi bir C ++ 11 özelliği kullanmaz ve E ve F nesnelerinin her ikisi de C ++ 11 desteğinin kararlı olduğu sürümleri kullanır. C ++ 17 desteği, kullanılan derleyici sürümlerinin hiçbirinde kararlı değildir, ancak yalnızca F nesnesi C ++ 17 özelliklerini kullanır ve bu nedenle diğer iki nesneyle uyumluluk sorunu yoktur (paylaştıkları tek özellik C ++ 03'ten gelir veya C ++ 11 ve kullanılan sürümler bu bölümleri iyi yapar). Daha sonra GCC 8 kullanarak dördüncü bir nesne olan G'yi derlemek istediyseniz ve -std=c++17F ve G'deki C ++ 17 sembolleri uyumsuz olduğundan, F'yi aynı sürümle (veya F'ye bağlamadan) yeniden derlemeniz gerekir.

Yukarıda D, E ve F arasında açıklanan uyumluluk için tek uyarı, programınızın libstdc++.soGCC 7'den (veya daha sonra) paylaşılan kitaplığı kullanması gerektiğidir . F nesnesi GCC 7 ile derlendiğinden, bu sürümdeki paylaşılan kitaplığı kullanmanız gerekir, çünkü programın herhangi bir bölümünü GCC 7 ile derlemek libstdc++.so, GCC 4.9 veya GCC 5'te bulunmayan simgelere bağımlılıklar getirebilir . Benzer şekilde, GCC 8 ile oluşturulmuş G nesnesine bağlandıysanız, G'nin libstdc++.soihtiyaç duyduğu tüm simgelerin bulunduğundan emin olmak için GCC 8'den kullanmanız gerekir . Basit kural, programın çalışma zamanında kullandığı paylaşılan kitaplığın en az nesnelerden herhangi birini derlemek için kullanılan sürüm kadar yeni olmasını sağlamaktır.

GCC'yi kullanırken, sorunuzla ilgili yorumlarda daha önce bahsedilen bir diğer uyarı, GCC 5'ten bu yana libstdc ++ 'da mevcut olan iki uygulamanınstd::string mevcut olmasıdır. Bu iki uygulama bağlantı uyumlu değildir (farklı karıştırılmış adlara sahiptirler, bu nedenle birbirine bağlanamazlar) ancak aynı ikili dosyada birlikte var olabilirler (farklı karıştırılmış adlara sahiptirler, bu nedenle bir nesne std::stringve diğer kullanımlar std::__cxx11::string). Nesneleriniz kullanıyorsa, std::stringgenellikle hepsi aynı dize uygulamasıyla derlenmelidir. -D_GLIBCXX_USE_CXX11_ABI=0Orijinal gcc4-compatibleuygulamayı -D_GLIBCXX_USE_CXX11_ABI=1seçmek veya yeni cxx11uygulamayı seçmek için ile derleyin (isme aldanmayın, C ++ 03'te de kullanılabilir, buna denircxx11çünkü C ++ 11 gereksinimlerine uygundur). Hangi uygulamanın varsayılan olduğu, GCC'nin nasıl yapılandırıldığına bağlıdır, ancak varsayılan, her zaman makroyla derleme sırasında geçersiz kılınabilir.


"çünkü programın herhangi bir bölümünü GCC 7 ile derlemek libstdc ++ 'da bulunan simgelere bağımlılık getirebilir. Bu nedenle GCC 4.9 veya GCC 5'ten" GCC 4.9 veya GCC 5'te mevcut OLMADIĞINI kastettiniz, değil mi? Bu aynı zamanda statik bağlantı için de geçerli mi? Derleyici sürümleri arasındaki uyumluluk hakkında bilgi için teşekkür ederiz.
Hadi Brais

1
Bu soruya bir ödül teklif etmenin büyük bir kusuru olduğunu fark ettim. 😂
Yörüngede Hafiflik Yarışları

4
@ricab Clang / libc ++ için cevabın aynı olduğundan% 90 eminim, ancak MSVC hakkında hiçbir fikrim yok.
Jonathan Wakely

1
Bu cevap yıldızdır. 5.0+ sürümünün 11/14 için kararlı olduğu bir yerde belgelendi mi?
Barry

1
Çok net değil veya tek bir yerde. gcc.gnu.org/gcc-5/changes.html#libstdcxx ve gcc.gnu.org/onlinedocs/libstdc++/manual/api.html#api.rel_51 , C ++ 11 için kütüphane desteğinin eksiksiz olduğunu beyan eder (dil destek özelliği daha önce tamamlanmıştı, ancak yine de "deneysel" idi). C ++ 14 kütüphane desteği 6.1'e kadar hala deneysel olarak listeleniyor, ancak pratikte 5.x ile 6.x arasında ABI'yi etkileyen hiçbir şeyin değişmediğini düşünüyorum.
Jonathan Wakely

17

Cevabın iki bölümü var. Derleyici düzeyinde uyumluluk ve bağlayıcı düzeyinde uyumluluk. Eski ile başlayalım.

tüm başlıkların C ++ 11'de yazıldığını varsayalım

Aynı derleyiciyi kullanmak, aynı standart kitaplık üstbilgisinin ve kaynak dosyalarının (derleyiciyle ilişkilendirilmiş onces) hedef C ++ standardına bakılmaksızın kullanılacağı anlamına gelir. Bu nedenle, standart kitaplığın başlık dosyaları, derleyici tarafından desteklenen tüm C ++ sürümleriyle uyumlu olacak şekilde yazılır.

Bununla birlikte, bir çeviri birimini derlemek için kullanılan derleyici seçenekleri belirli bir C ++ standardını belirtiyorsa, yalnızca yeni standartlarda mevcut olan herhangi bir özelliğe erişilememelidir. Bu, __cplusplusdirektif kullanılarak yapılır . Nasıl kullanıldığına dair ilginç bir örnek için vektör kaynak dosyasına bakın . Benzer şekilde, derleyici standardın daha yeni sürümleri tarafından sunulan sözdizimsel özellikleri reddedecektir.

Tüm bunlar, varsayımınızın yalnızca yazdığınız başlık dosyalarına uygulanabileceği anlamına gelir. Bu başlık dosyaları, farklı C ++ standartlarını hedefleyen farklı çeviri birimlerine dahil edildiğinde uyumsuzluklara neden olabilir. Bu, C ++ standardının Ek C'de tartışılmaktadır. 4 cümle var, sadece ilkini tartışacağım ve geri kalanından kısaca bahsedeceğim.

C.3.1 Madde 2: sözcük kuralları

Tek tırnak işaretleri, C ++ 11'de bir karakter hazır bilgisini sınırlarken, bunlar C ++ 14 ve C ++ 17'de rakam ayırıcılardır. Saf C ++ 11 başlık dosyalarından birinde aşağıdaki makro tanımına sahip olduğunuzu varsayalım:

#define M(x, ...) __VA_ARGS__

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

Başlık dosyasını içeren, ancak sırasıyla C ++ 11 ve C ++ 14'ü hedefleyen iki çeviri birimini düşünün. C ++ 11 hedeflenirken, tırnak işaretleri içindeki virgül bir parametre ayırıcı olarak kabul edilmez; sadece bir kez parametre vardır. Bu nedenle, kod şuna eşdeğer olacaktır:

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

Öte yandan, C ++ 14 hedeflenirken, tek tırnaklar basamak ayırıcılar olarak yorumlanır. Bu nedenle, kod şuna eşdeğer olacaktır:

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

Buradaki nokta, saf C ++ 11 başlık dosyalarından birinde tek tırnak kullanmanın, C ++ 14 / 17'yi hedefleyen çeviri birimlerinde şaşırtıcı hatalara neden olabileceğidir. Bu nedenle, bir başlık dosyası C ++ 11'de yazılsa bile, standardın sonraki sürümleriyle uyumlu olduğundan emin olmak için dikkatli bir şekilde yazılmalıdır. __cplusplusDirektif burada yararlı olabilir.

Standarttaki diğer üç madde şunları içerir:

C.3.2 Madde 3: temel kavramlar

Değişiklik : Yeni olağan (yerleştirme dışı) ayırıcı

Gerekçe : Boyut ayırma için gereklidir.

Orijinal özellik üzerindeki etki : Geçerli C ++ 2011 kodu, aşağıdaki gibi bir genel yerleşim ayırma işlevi ve serbest bırakma işlevi bildirebilir:

void operator new(std::size_t, std::size_t); 
void operator delete(void*, std::size_t) noexcept;

Bununla birlikte, bu Uluslararası Standartta, operatör silme beyanı, önceden tanımlanmış bir olağan (yerleştirme olmayan) operatör silme (3.7.4) ile eşleşebilir. Eğer öyleyse, program, sınıf üyesi tahsis fonksiyonları ve serbest bırakma fonksiyonları (5.3.4) için olduğu gibi biçimsizdir.

C.3.3 Madde 7: beyanlar

Değiştir : constexpr statik olmayan üye işlevleri örtük olarak const üye işlevleri değildir.

Gerekçe : constexpr üye işlevlerinin nesneyi değiştirmesine izin vermek için gereklidir.

Orijinal özellik üzerindeki etki : Geçerli C ++ 2011 kodu bu Uluslararası Standartta derlenemeyebilir.

Örneğin, aşağıdaki kod C ++ 2011'de geçerlidir ancak bu Uluslararası Standartta geçersizdir çünkü aynı üye işlevini farklı dönüş türleriyle iki kez bildirir:

struct S {
constexpr const int &f();
int &f();
};

C.3.4 Madde 27: girdi / çıktı kitaplığı

Değişiklik : alır tanımlı değil.

Gerekçe : Get kullanımı tehlikeli kabul edilir.

Orijinal özellik üzerindeki etki : gets işlevini kullanan geçerli C ++ 2011 kodu bu Uluslararası Standartta derlenemeyebilir.

C ++ 14 ve C ++ 17 arasındaki olası uyumsuzluklar C.4'te tartışılmaktadır. Standart olmayan tüm üstbilgi dosyaları (soruda belirtildiği gibi) C ++ 11'de yazıldığı için bu sorunlar ortaya çıkmayacağı için burada bunlardan bahsetmeyeceğim.

Şimdi bağlayıcı düzeyinde uyumluluğu tartışacağım. Genel olarak, uyumsuzlukların olası nedenleri arasında şunlar yer alır:

  • Nesne dosyalarının biçimi.
  • Program başlatma ve sonlandırma rutinleri ve maingiriş noktası.
  • Tüm program optimizasyonu (WPO).

Elde edilen nesne dosyasının formatı hedef C ++ standardına bağlıysa, bağlayıcının farklı nesne dosyalarını bağlayabilmesi gerekir. GCC, LLVM ve VC ++ 'da neyse ki durum böyle değil. Yani, nesnelerin dosyalarının biçimi, derleyicinin kendisine büyük ölçüde bağımlı olmasına rağmen, hedef standarttan bağımsız olarak aynıdır. Aslında, GCC, LLVM ve VC ++ bağlayıcılarından hiçbiri hedef C ++ standardı hakkında bilgi gerektirmez. Bu ayrıca, önceden derlenmiş nesne dosyalarını bağlayabileceğimiz anlamına gelir (çalışma zamanını statik olarak bağlayarak).

Program başlatma yordamı (çağıran işlev main) farklı C ++ standartları için farklıysa ve farklı yordamlar birbiriyle uyumlu değilse, nesne dosyalarını bağlamak mümkün olmayacaktır. GCC, LLVM ve VC ++ 'da neyse ki durum böyle değil. Ek olarak, mainişlevin imzası (ve onun üzerinde geçerli olan kısıtlamalar, standardın 3.6 Bölümüne bakın) tüm C ++ standartlarında aynıdır, dolayısıyla hangi çeviri biriminde bulunduğu önemli değildir.

Genel olarak, WPO, farklı C ++ standartları kullanılarak derlenen nesne dosyalarıyla iyi çalışmayabilir. Bu, derleyicinin tam olarak hangi aşamalarının hedef standart hakkında bilgi gerektirdiğine ve hangi aşamaların gerektirmediğine ve nesneler arasındaki prosedürler arası optimizasyonlar üzerindeki etkisine bağlıdır. Neyse ki, GCC, LLVM ve VC ++ iyi tasarlanmış ve bu soruna sahip değil (bildiğim kadarıyla değil).

Bu nedenle, GCC, LLVM ve VC ++ , C ++ standardının farklı sürümleri arasında ikili uyumluluğu sağlamak için tasarlanmıştır . Yine de bu, standardın kendisinin bir gereği değildir.

Bu arada, VC ++ derleyicisi , C ++ standardının belirli bir sürümünü hedeflemenizi sağlayan std anahtarını sunsa da , C ++ 11'i hedeflemeyi desteklemez. Belirtilebilecek minimum sürüm, Visual C ++ 2013 Güncellemesi 3'ten başlayarak varsayılan olan C ++ 14'tür. C ++ 11'i hedeflemek için daha eski bir VC ++ sürümü kullanabilirsiniz, ancak daha sonra farklı VC ++ derleyicileri kullanmanız gerekir. en azından WPO'yu bozacak olan C ++ standardının farklı sürümlerini hedefleyen farklı çeviri birimlerini derlemek.

CAVEAT: Cevabım tam veya çok kesin olmayabilir.


Soru gerçekten derlemeden ziyade bağlantı kurmayla ilgiliydi. Bunun belki net olmadığını anladım ( bu yorum sayesinde ) ve dahil edilen başlıkların her üç standartta da aynı yoruma sahip olduğunu netleştirmek için düzenledim.
ricab

@ricab Cevap hem derlemeyi hem de bağlamayı kapsar. İkisini de sorduğunu sanıyordum.
Hadi Brais

1
Gerçekten, ama cevabın çok uzun ve kafa karıştırıcı olduğunu düşünüyorum, özellikle "Şimdi bağlayıcı seviyesinde uyumluluğu tartışacağım" a kadar. Bunun üzerindeki her şeyi, örneğin dahil edilen başlıkların C ++ 11 ve C ++ 14 / 17'de aynı anlama sahip olduğu varsayılamazsa, ilk etapta bunları dahil etmek güvenli değildir . Kalan kısım için, bu üç madde işaretinin uyumsuzluğun tek potansiyel nedeni olduğunu gösteren bir kaynağınız var mı? Her durumda cevabınız için teşekkürler, hala oy
veriyorum

@ricab Kesin olarak söyleyemem. Bu yüzden cevabın sonuna uyarı ekledim. Bir şeyi kaçırmış olsam da başka herkes cevabı daha kesin veya eksiksiz hale getirebilir.
Hadi Brais

Bu beni karıştırıyor: "Aynı derleyiciyi kullanmak, aynı standart kitaplık başlığı ve kaynak dosyalarının (...) kullanılacağı anlamına gelir". Bu nasıl olabilir? Gcc5 ile derlenmiş eski kodum varsa, o sürüme ait olan 'derleyici dosyaları' ileride kanıtlanamaz. Farklı derleyici sürümleriyle (çılgınca) farklı zamanlarda derlenen kaynak kodu için, kitaplık başlığının ve kaynak dosyalarının farklı olduğundan oldukça emin olabiliriz. Bunların aynı olması kuralına göre, eski kaynak kodunu gcc5 ile yeniden derlemelisin, ... ve hepsinin en son (aynı) 'derleyici dosyalarını' kullandığından emin olmalısın.
user2943111

2

Yeni C ++ standartları iki bölümden oluşmaktadır: dil özellikleri ve standart kitaplık bileşenleri.

Yeni standarttan kastettiğin gibi , dilin kendisindeki değişiklikler (örneğin aralıklı) neredeyse hiç sorun yok (bazen yeni standart dil özelliklerine sahip 3. taraf kitaplık başlıklarında çakışmalar olabilir).

Ama standart kütüphane ...

Her derleyici sürümü, C ++ standart kitaplığı (gcc ile libstdc ++, clang ile libc ++, VC ++ ile MS C ++ standart kitaplığı, ...) ve her standart sürüm için çok fazla uygulama olmadan tam olarak bir uygulama ile birlikte gelir. Ayrıca bazı durumlarda, standart kitaplığın sağlanan derleyici dışında başka bir uygulaması da kullanabilirsiniz. Dikkat etmeniz gereken şey, eski bir standart kütüphane uygulamasını yenisiyle ilişkilendirmektir.

Üçüncü taraf kitaplıklar ile kodunuz arasında meydana gelebilecek çelişki, söz konusu üçüncü taraf kitaplıklara bağlanan standart kitaplıktır (ve diğer kitaplıklardır).


"Her derleyici sürümü bir STL uygulamasıyla birlikte gelir" Hayır yok
Orbit'te Hafiflik Yarışları

@LightnessRacesinOrbit Örneğin libstdc ++ ve gcc arasında bir ralasyon olmadığını mı söylüyorsunuz?
E.Vakili

9
Hayır, demek istediğim, STL sadece yirmi yıldan fazla bir süredir modası geçmiş durumda. C ++ Standart Kitaplığı'nı kastettiniz. Cevabın geri kalanına gelince, talebinizi desteklemek için bazı referanslar / kanıtlar sağlayabilir misiniz? Böyle bir soru için önemli olduğunu düşünüyorum.
Yörüngede Hafiflik Yarışları

3
Maalesef hayır, metinden anlaşılmıyor. Bazı ilginç iddialarda bulundunuz, ancak bunları henüz herhangi bir kanıtla desteklemediniz.
Yörüngede Hafiflik Yarışları
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.