C ++ kodu hem C ++ 03 hem de C ++ 11'de geçerli olabilir, ancak farklı şeyler yapabilir mi?


299

C ++ kodunun hem C ++ 03 standardına hem de C ++ 11 standardına uyması mümkün , ancak hangi standardın derlendiğine bağlı olarak farklı şeyler yapabilir mi?


26
Eminim böyle autobir durumla sonuçlanabilir
OMGtechy

8
Evet. Bir örnek >>şablonda kullanıldığındadır. Her iki standart için de derlenebileceği bir durum ortaya çıkarabilirsiniz. Eminim ki değişiklikleri bulmak kolay olurdu başka bir başlatma olduğunu.
chris

5
İşte >> durum hakkında güzel bir makale: gustedt.wordpress.com/2013/12/15/…
chris

6
@OMGtechy: Buna neden olabileceğini sanmıyorum auto . Eski anlamıyla, bir autobildiri bir tür adı gerektirir; yeni anlamıyla, bir tür adına izin verilmez.
Keith Thompson

2
Nasıl açık uçlu? Başka bir soru ile kendiniz de bu sorunun cevabının "evet, işte nasıl bir örnek" olduğuna dikkat çektiniz. Siz de belirttiğiniz gibi, sorunun çok kesin bir cevabı var.
jalf

Yanıtlar:


284

Cevap kesin bir evet. Artı tarafta:

  • Önceden örtük olarak kopyalanan nesneler artık mümkün olduğunda bunları örtük olarak taşıyacaktır.

Olumsuz tarafta, standardın ek C'sinde birkaç örnek listelenmiştir. Olumlu olmaktan çok daha olumsuz olanlar olsa da, her birinin meydana gelme olasılığı daha azdır.

Dize değişmez değerleri

#define u8 "abc"
const char* s = u8"def"; // Previously "abcdef", now "def"

ve

#define _x "there"
"hello "_x // Previously "hello there", now a user defined string literal

0 dönüşümlerini yazın

C ++ 11'de yalnızca değişmezler tamsayı null işaretçi sabitleridir:

void f(void *); // #1
void f(...); // #2
template<int N> void g() {
    f(0*N); // Calls #2; used to call #1
}

Tamsayı bölme ve modulo sonrasında yuvarlak sonuçlar

C ++ 03'te derleyicinin 0'a veya negatif sonsuza doğru yuvarlanmasına izin verildi. C ++ 11'de 0'a yuvarlamak zorunludur

int i = (-1) / 2; // Might have been -1 in C++03, is now ensured to be 0

İç içe şablon kapanış ayraçları arasındaki boşluklar >> vs>>

Bir uzmanlık veya örnekleme içinde >>bunun yerine C ++ 03'te sağa kaydırma olarak yorumlanabilir. Bu mevcut kodu kırma olasılığı daha yüksektir: ( http://gustedt.wordpress.com/2013/12/15/a-disimprovement-observed-from-the-outside-right-angle-brackets/ )

template< unsigned len > unsigned int fun(unsigned int x);
typedef unsigned int (*fun_t)(unsigned int);
template< fun_t f > unsigned int fon(unsigned int x);

void total(void) {
    // fon<fun<9> >(1) >> 2 in both standards
    unsigned int A = fon< fun< 9 > >(1) >>(2);
    // fon<fun<4> >(2) in C++03
    // Compile time error in C++11
    unsigned int B = fon< fun< 9 >>(1) > >(2);
}

Operatör newartık başka istisnalar atabilirstd::bad_alloc

struct foo { void *operator new(size_t x){ throw std::exception(); } }
try {
    foo *f = new foo();
} catch (std::bad_alloc &) {
    // c++03 code
} catch (std::exception &) {
    // c++11 code
}

Kullanıcı ilan yıkıcılar örtük bir istisna şartname var dan örnek kırılma değişiklikler ++ 11 C tanıtıldı sen?

struct A {
    ~A() { throw "foo"; } // Calls std::terminate in C++11
};
//...
try { 
    A a; 
} catch(...) { 
    // C++03 will catch the exception
} 

size() artık konteynerlerin O (1)

std::list<double> list;
// ...
size_t s = list.size(); // Might be an O(n) operation in C++03

std::ios_base::failurestd::exceptionartık direkt olarak türetmiyor

Doğrudan temel sınıf yeni olsa da std::runtime_errordeğil. Böylece:

try {
    std::cin >> variable; // exceptions enabled, and error here
} catch(std::runtime_error &) {
    std::cerr << "C++11\n";
} catch(std::ios_base::failure &) {
    std::cerr << "Pre-C++11\n";
}

11
Güzel, +1. Bir diğeri, bir kullanıcı örtülü olarak şimdi yıkıcı ilan olmasıdır noexecpt(true), böylece throwartık arayacak bir yıkıcı std::terminate. Ama umarım böyle bir kod yazan herkes bu konuda mutlu olur!
typ1232

4
Ancak std :: system_error (dolaylı olarak) std :: istisnasından türetilmiştir, bu yüzden catch (std::exception &)hala yakalar std::ios_base::failure.
user2665887

@ user2665887 haklısın. yine de bir programın davranışını etkileyebilir, ancak şu anda minimal bir örnek düşünemiyorum.
örnek

4
Çok şaşırdım, söyledikleriniz operator newdoğru (şimdi atabiliyor std::bad_array_new_length), ancak örneğiniz bunu hiç göstermiyor. Gösterdiğiniz kod C ++ 03 ve C ++ 11 AFAIK için aynıdır.
Mooing Ördek

2
Listenin kapak tarafı :: boyut O (1) olmak, ekin şimdi O (n)
Tony Delroy

56

Bu makaleye ve hala her ikisinde derlenirken C ++ 03'ten C ++ 11'e anlamını nasıl değiştirebileceğinin güzel bir örneğine sahip olan izlemeye işaret ediyorum >>.

bool const one = true;
int const two = 2;
int const three = 3;

template<int> struct fun {
    typedef int two;
};

template<class T> struct fon {
    static int const three = ::three;
    static bool const one = ::one;
};

int main(void) {
    fon< fun< 1 >>::three >::two >::one; // valid for both  
}

Anahtar kısım main, bir ifade olan satırdır .

C ++ 03'te:

1 >> ::three = 0
=> fon< fun< 0 >::two >::one;

fun< 0 >::two = int
=> fon< int >::one

fon< int >::one = true
=> true

C ++ 11'de

fun< 1 > is a type argument to fon
fon< fun<1> >::three = 3
=> 3 > ::two > ::one

::two is 2 and ::one is 1
=> 3 > 2 > 1
=> (3 > 2) > 1
=> true > 1
=> 1 > 1
=> false

Tebrikler, aynı ifade için iki farklı sonuç. Verilen C ++ 03, test ettiğimde Clang bir uyarı formu buldu.


buna ihtiyaç duymaz bu garip typenameiçin ::twoC ++ 03 sürümü
zahir

3
Güzel standart, farklı standartlar için trueveya falsefarklı standartlar için değerlendirmek için kaynatılması . Belki bir özellik testi olarak kullanabiliriz </joke>
cmaster - reinstate monica

@zahir, Bu bir tür değil, sadece bir değer.
chris

iyi, uygun cmdline seçenekleri bunun hakkında uyarır ( warning: comparisons like ‘X<=Y<=Z’ do not have their mathematical meaning [-Wparentheses]), ancak belirsiz ::operatörün anlamını nasıl değiştirdiğine dair güzel bir örnek (ya küresel kapsama atıfta bulunarak ya da hemen önünde
örnek

@example, Şaşırtıcı bir şekilde, GCC bu uyarıyı verir, ancak Clang vermez.
chris

39

Evet, aynı kodun C ++ 03 ve C ++ 11 arasında farklı davranışlarla sonuçlanmasına neden olacak değişiklik sayısı vardır. Sıralama kuralları farklılıkları, önceden tanımlanmamış bazı davranışların iyi tanımlanması da dahil olmak üzere bazı ilginç değişiklikler yapar.

1. bir başlatıcı listesinde aynı değişkenin birden fazla mutasyon

Çok ilginç bir köşe örneği, bir başlatıcı listesinde aynı değişkenin birden fazla mutasyonunu yapar, örneğin:

int main()
{
    int count = 0 ;
    int arrInt[2] = { count++, count++ } ;

    return 0 ;
}

Hem C ++ 03 hem de C ++ 11'de bu iyi tanımlanmıştır, ancak C ++ 03'teki değerlendirme sırası belirsizdir, ancak C ++ 11'de göründükleri sırayla değerlendirilirler . Bu yüzden clangC ++ 03 modunda kullanarak derlersek aşağıdaki uyarıyı sağlar ( canlı olarak görün ):

warning: multiple unsequenced modifications to 'count' [-Wunsequenced]

    int arrInt[2] = { count++, count++ } ;

                           ^        ~~

ancak C ++ 11'de bir uyarı sağlamaz ( canlı yayına bakın ).

2. Yeni sıralama kuralları i = ++ i + 1 yapar; C ++ 11'de iyi tanımlanmış

C ++ 03'ten sonra kabul edilen yeni sıralama kuralları şu anlama gelir:

int i = 0 ;
i = ++ i + 1;

artık C ++ 11'de tanımlanmamış bir davranış değildir, bu durum 637 hata raporunda ele alınmıştır. Sıralama kuralları ve örnek katılmıyorum

3. Yeni sıralama kuralları da ++++ i yapar; C ++ 11'de iyi tanımlanmış

C ++ 03'ten sonra kabul edilen yeni sıralama kuralları şu anlama gelir:

int i = 0 ;
++++i ;

artık C ++ 11'de tanımlanmamış bir davranış değildir.

4. Biraz Daha Duyarlı İmzalı Sol Vites

Daha sonra C ++ 11 taslakları, N3485aşağıda bağladığım , 1 biti işaret bitine veya işaret bitine geçirmenin tanımlanmamış davranışını düzeltti . Bu durum ayrıca 1457 hata raporunda da yer almaktadır . Howard Hinnant , C ++ 11'de sol kayma (<<) negatif tamsayı tanımsız bir davranış mıdır? Konusundaki bu değişikliğin önemini yorumladı. .

5. constexpr fonksiyonları C ++ 11 derleme zaman sabiti ifadeleri olarak ele alınabilir

C ++ 11, aşağıdakileri içeren constexpr işlevlerini tanıttı :

Constexpr belirteci, derleme zamanında işlevin veya değişkenin değerini değerlendirmenin mümkün olduğunu bildirir. Bu tür değişkenler ve fonksiyonlar daha sonra sadece derleme zaman sabiti ifadelerine izin verildiğinde kullanılabilir.

C ++ 03 constexpr özelliğine sahip olmasa da , standart kitaplık C ++ 11'de constexpr olarak birçok işlev sağladığı için açıkça constexpr anahtar sözcüğünü kullanmak zorunda değiliz . Örneğin std :: numeric_limits :: min . Farklı davranışlara yol açabilir, örneğin:

#include <limits>

int main()
{
    int x[std::numeric_limits<unsigned int>::min()+2] ;
}

Kullanma clangneden olur 03, bu C ++ xolan bir değişken uzunluklu dizi olmak bir uzantısı ve aşağıdaki uyarıyı oluşturur:

warning: variable length arrays are a C99 feature [-Wvla-extension]
    int x[std::numeric_limits<unsigned int>::min()+2] ;
         ^

C ++ 11 std::numeric_limits<unsigned int>::min()+2ise derleme zamanı sabit bir ifadedir ve VLA uzantısını gerektirmez.

6. C ++ 11'de, yıkıcılarınız için dolaylı istisna özellikleri dolaylı olarak oluşturulur

C ++ 11'de kullanıcı tanımlı yıkıcı, istisnasız yıkıcılardanoexcept(true) açıklandığı gibi örtülü spesifikasyona sahip olduğundan, aşağıdaki programın anlamı:

#include <iostream>
#include <stdexcept>

struct S
{
  ~S() { throw std::runtime_error(""); } // bad, but acceptable
};

int main()
{
  try { S s; }
  catch (...) {
    std::cerr << "exception occurred";
  } 
 std::cout << "success";
}

C ++ 11'de arayacak std::terminateancak C ++ 03'te başarıyla çalışacaktır.

7. C ++ 03'te, şablon bağımsız değişkenlerinin dahili bağlantısı olamazdı

Bu güzel kaplıdır Why std :: karşılaştırın sınıfları kabul etmeyen sıralama Bir işlev içinde bildirilen . Bu nedenle, aşağıdaki kod C ++ 03'de çalışmamalıdır:

#include <iostream>
#include <vector>
#include <algorithm>

class Comparators
{
public:
    bool operator()(int first, int second)
    {
        return first < second;
    }
};

int main()
{
    class ComparatorsInner : public Comparators{};

    std::vector<int> compares ;
    compares.push_back(20) ;
    compares.push_back(10) ;
    compares.push_back(30) ;

    ComparatorsInner comparatorInner;
    std::sort(compares.begin(), compares.end(), comparatorInner);

    std::vector<int>::iterator it;
    for(it = compares.begin(); it != compares.end(); ++it)
    {
        std::cout << (*it) << std::endl;
    }
}

ancak şu anda clangbu kod C ++ 03 modunda izin veriyorsa -pedantic-errors, biraz icky olan bayrağı kullanmazsanız , canlı olarak görün .

8. >> birden fazla şablon kapatılırken artık kötü biçimlendirilmemiş

>>Birden çok şablonu kapatmak için kullanmak artık yanlış biçimlendirilmiş değildir ancak C ++ 03 ve C + 11'de farklı sonuçlara sahip kodlara yol açabilir. Aşağıdaki örnek Sağ açılı ayraçlardan ve geriye dönük uyumluluktan alınmıştır :

#include <iostream>
template<int I> struct X {
  static int const c = 2;
};
template<> struct X<0> {
  typedef int c;
};
template<typename T> struct Y {
  static int const c = 3;
};
static int const c = 4;
int main() {
  std::cout << (Y<X<1> >::c >::c>::c) << '\n';
  std::cout << (Y<X< 1>>::c >::c>::c) << '\n';
}

ve C ++ 03'teki sonuç:

0
3

ve C ++ 11'de:

0
0

9. C ++ 11 std :: vector yapıcılarının bazılarını değiştirir

Bu yanıttan biraz değiştirilmiş kod , std :: vector'dan aşağıdaki kurucuyu kullanmanın :

std::vector<T> test(1);

C ++ 03 ve C ++ 11'de farklı sonuçlar üretir:

#include <iostream>
#include <vector>

struct T
{
    bool flag;
    T() : flag(false) {}
    T(const T&) : flag(true) {}
};


int main()
{
    std::vector<T> test(1);
    bool is_cpp11 = !test[0].flag;

    std::cout << is_cpp11 << std::endl ;
}

10. Toplu başlatıcılarda dönüşümleri daraltma

C ++ 11'de, toplam başlatıcılardaki daralma dönüşümü kötü biçimlendirilmiştir ve gccC ++ 11'de varsayılan olarak bir uyarı sağlamasına rağmen hem C ++ 11 hem de C ++ 03'te buna izin verir gibi görünüyor :

int x[] = { 2.0 };

Bu taslak C ++ 11 standart bölümünde 8.5.4 Liste başlatma paragrafı 3'te ele alınmıştır :

Bir nesnenin listeye başlatılması veya T tip referansı aşağıdaki gibi tanımlanır:

ve aşağıdaki madde işaretini içerir ( vurgu mayın ):

Aksi takdirde, T bir sınıf tipiyse yapıcılar dikkate alınır. Uygulanabilir yapıcılar numaralandırılır ve en iyisi aşırı yük çözünürlüğü (13.3, 13.3.1.7) ile seçilir. Bağımsız değişkenlerden herhangi birini dönüştürmek için daraltma dönüşümü (aşağıya bakın) gerekiyorsa, program kötü biçimlendirilmiştir

Bu ve daha birçok örnek taslak C ++ standart bölümü annex C.2 C ++ ve ISO C ++ 2003'te ele alınmıştır . Ayrıca şunları içerir:

  • Yeni dize değişmezleri [...] Özellikle, R, u8, u8R, u, uR, U, UR veya LR adlı makrolar bir dize değişmezine bitişikken genişletilmez, ancak dize değişmezinin bir parçası olarak yorumlanır . Örneğin

    #define u8 "abc"
    const char *s = u8"def"; // Previously "abcdef", now "def"
    
  • Kullanıcı tanımlı değişmez dize desteği [...] Daha önce, # 1 iki ayrı önişleme belirtecinden oluşacaktı ve _x makrosu genişletilecekti. Bu Uluslararası Standartta, # 1 tek bir önişleme belirteçinden oluşur, bu nedenle makro genişletilmez.

    #define _x "there"
    "hello"_x // #1
    
  • Tamsayı bölme kullanan tamsayı / ve% [...] 2003 kod sonuçları için yuvarlamayı belirtin, sonucu 0'a veya negatif sonsuza doğru yuvarlar, oysa bu Uluslararası Standart her zaman sonucu 0'a yuvarlar.

  • Size () üye işlevlerinin karmaşıklığı artık sabittir [...] C ++ 2003 ile uyumlu bazı kap uygulamaları bu Uluslararası Standartta belirtilen boyut () gereksinimlerine uymayabilir. Std :: list gibi kapların daha katı gereksinimlere göre ayarlanması uyumsuz değişiklikler gerektirebilir.

  • Std :: ios_base :: başarısızlık temel sınıfını değiştirin [...] std :: ios_base :: başarısızlık artık doğrudan std :: istisnadan türetilmiyor, ancak artık std :: system_error kaynağından türetiliyor. std :: runtime_error. Std :: ios_base :: hatasının doğrudan std :: istisnadan türetildiğini varsayan geçerli C ++ 2003 kodu, bu Uluslararası Standartta farklı şekilde çalışabilir.


Peki, örneklerin çoğu, daha önce tanımlanmamış davranışın şimdi iyi tanımlanmış olduğu gerçeğine dayandırılıyor mu?
MatthiasB

@MatthiasB 2, 3 ve 4 bununla ilgilidir, bu noktada artık örneklerin çoğunluğu değildir. Daha fazla eklediğimden daha küçük bir set haline geleceğinden daha fazla tanımlanmamış davranış örneği bulacağımdan şüpheliyim.
Shafik Yaghmour

Eh, # 1 davranış belirtilmemiş, bu yüzden tanımsız davranış olarak sayılır (en azından c ++ 03 ile belirli bir sonuç elde edemezsiniz, şimdi c ++ 11 ile yapabilirsiniz), # 5 c ++ 'ın standart uzantısı. Ama sanırım haklısın. Ne kadar çok bakarsanız, her iki standartta da tanımlanan, ancak farklı sonuçlar üreten daha fazla örnek bulacaksınız.
MatthiasB

@MatthiasB evet, hem tanımlanmamış hem de tanımsız davranışlar istenmeyen sonuçlara yol açar. Linux'u dikkate alan uzantılara gelince, önemli oldukları dünyada varsaymamız gereken bir dizi gcc uzantısına bağlıdır . Bu soruyu ilk cevapladığımda çok fazla örnek bulmayı beklemiyordum.
Shafik Yaghmour

35

Potansiyel olarak tehlikeli geriye dönük uyumsuz değişikliklerden biri std::vector, özellikle başlangıç ​​boyutunu belirten aşırı yüklenme gibi dizi kaplarının yapıcılarındadır . C ++ 03'te, varsayılan olarak oluşturulmuş bir öğeyi kopyaladıklarında, C ++ 11'de her birini varsayılan olarak yapılandırırlar.

Bu örneği düşünün ( boost::shared_ptrgeçerli C ++ 03 olacak şekilde kullanarak):

#include <deque>
#include <iostream>

#include "boost/shared_ptr.hpp"


struct Widget
{
  boost::shared_ptr<int> p;

  Widget() : p(new int(42)) {}
};


int main()
{
  std::deque<Widget> d(10);
  for (size_t i = 0; i < d.size(); ++i)
    std::cout << "d[" << i << "] : " << d[i].p.use_count() << '\n';
}

C ++ 03 Canlı örneği

C ++ 11 Canlı örneği

Bunun nedeni, C ++ 03'ün hem "boyut ve prototip öğesini belirtin" hem de "yalnızca boyutu belirtin" için bir aşırı yük belirtmesidir (kısalık için atlanan ayırıcı bağımsız değişkenleri):

container(size_type size, const value_type &prototype = value_type());

Bu her zaman prototypekonteyner sizesürelerine kopyalanır . Bu nedenle, yalnızca bir bağımsız değişkenle çağrıldığında, sizevarsayılan olarak oluşturulmuş bir öğenin kopyalarını oluşturur .

C ++ 11'de, bu yapıcı imzası kaldırıldı ve aşağıdaki iki aşırı yükleme ile değiştirildi:

container(size_type size);

container(size_type size, const value_type &prototype);

İkincisi daha önce olduğu gibi sizeçalışarak prototypeöğenin kopyalarını oluşturur . Ancak, ilki (şimdi yalnızca belirtilen boyut bağımsız değişkeniyle çağrıları işleyen) varsayılan olarak her öğeyi ayrı ayrı yapılandırır.

Bu değişikliğin sebebi, C ++ 03 aşırı yüklenmesinin yalnızca bir öğe türü ile kullanılamayacağı yönündedir. Ama bu daha az kırılgan bir değişiklik ve nadiren bir tanesi bunu belgeledi.


3
Bu bariz bir değişiklik olsa da, ben C ++ 11 davranış tercih. Bunun dequeaynı kaynağı paylaşan on widget değil, on ayrı widget holding ile sonuçlanmasını beklerdim .
Agentlien

19

Adresinden başarısız bir okuma sonucu std::istreamdeğişti. CppReference güzelce özetliyor:

Çıkarma başarısız olursa (örn. Bir rakamın beklendiği yerde bir harf girilmişse) valuedeğiştirilmeden bırakılır ve failbitayarlanır. (C ++ 11'e kadar)

Çıkarma işlemi başarısız olursa, sıfıra yazılır valueve failbitayarlanır. Çok büyük veya çok küçük değer çıkarma sonuçları sığacak şekilde olursa value, std::numeric_limits<T>::max()ya da std::numeric_limits<T>::min()yazılmıştır ve failbitbayrak ayarlanır. (C ++ 11'den beri)

Yeni semantiğe alışkınsanız ve daha sonra C ++ 03 kullanarak yazmak zorundaysanız, bu öncelikle bir sorundur. Aşağıdakiler özellikle iyi bir uygulama değildir, ancak C ++ 11'de iyi tanımlanmıştır:

int x, y;
std::cin >> x >> y;
std::cout << x + y;

Bununla birlikte, C ++ 03'te, yukarıdaki kod başlatılmamış bir değişken kullanır ve bu nedenle tanımlanmamış bir davranışa sahiptir.


4
C ++ 03'te bu standartlaştırılmış davranışı , olduğu gibi varsayılan bir değer sağlamak için kullanabileceğinizi ekleyebilirsiniz int x = 1, y = 1; cin >> x >> y; cout << x*y;. C ++ 03 ile, xhiçbiri yokunamadığında doğru bir şekilde üretilirdi.
cmaster - reinstate monica

15

Bu iş parçacığı Çalışma zamanında C ++ 03 ve C ++ 0x arasındaki farkların, dil farklılıklarını belirlemek için, örneğin C ++ 11 başvuru daraltmasından yararlanarak örnekleri vardır:

template <class T> bool f(T&) {return true; } 
template <class T> bool f(...){return false;} 

bool isCpp11() 
{
    int v = 1;
    return f<int&>(v); 
}

ve şablon tipleri olarak yerel türlere izin veren c ++ 11:

template <class T> bool cpp11(T)  {return true;} //T cannot be a local type in C++03
                   bool cpp11(...){return false;}

bool isCpp0x() 
{
   struct local {} var; //variable with local type
   return cpp11(var);
}

7

İşte başka bir örnek:

#include <iostream>

template<class T>
struct has {
  typedef char yes;
  typedef yes (&no)[2];    
  template<int> struct foo;    
  template<class U> static yes test(foo<U::bar>*);      
  template<class U> static no  test(...);    
  static bool const value = sizeof(test<T>(0)) == sizeof(yes);
};

enum foo { bar };

int main()
{
    std::cout << (has<foo>::value ? "yes" : "no") << std::endl;
}

Baskılar:

Using c++03: no
Using c++11: yes

Sonucu Coliru'da görün

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.