C ++ 0x'de dönüşümleri daraltma. Sadece ben miyim yoksa bu büyük bir değişiklik gibi mi geliyor?


86

C ++ 0x bir sözde gerektirdiğinden, aşağıdaki kod ve benzeri kod kötü şekillendirilmiş yapacak daralan dönüşüm a doublea int.

int a[] = { 1.0 };

Bu tür bir başlatmanın gerçek dünya kodunda çok kullanılıp kullanılmadığını merak ediyorum. Bu değişiklikle kaç kod kırılacak? Kodunuz etkilendiyse, bunu kodunuzda düzeltmek çok zahmetli mi?


Referans için n3225'in 8.5.4 / 6'sına bakın

Daralan bir dönüşüm örtük bir dönüşümdür

  • kayan nokta türünden tam sayı türüne veya
  • long double'dan double'a veya float'a veya double'dan float'a, ancak kaynağın sabit bir ifade olduğu ve dönüşümden sonraki gerçek değerin temsil edilebilecek değerler aralığı içinde olduğu durumlar (tam olarak temsil edilemese bile) veya
  • tamsayı türünden veya kapsamı belirlenmemiş numaralandırma türünden, kaynağın sabit bir ifade olduğu ve dönüşümden sonraki gerçek değerin hedef türüne sığacağı ve orijinal türüne geri dönüştürüldüğünde orijinal değeri üreteceği durumlar hariç, değişken nokta türüne
  • bir tamsayı türünden veya kapsamı kaldırılmış numaralandırma türünden, kaynağın sabit bir ifade olduğu ve dönüştürmeden sonraki gerçek değerin hedef türüne sığacağı ve orijinal değeri ne zaman üreteceği dışında, orijinal türün tüm değerlerini temsil edemeyen bir tam sayı türüne orijinal türe dönüştürülür.

1
Bunun yalnızca dahili türlerin başlatılması için geçerli olduğunu varsayarsak, bunun nasıl zarar vereceğini göremiyorum. Elbette, bu bazı kodları bozabilir. Ancak düzeltilmesi kolay olmalı.
Johan Kotlinski

1
@John Dibling: Hayır, değer tam olarak hedef türle temsil edilebildiğinde başlatma kötü biçimlendirilmez. (Ve 0zaten zaten bir int.)
aschepler

2
@Nim: Bunun yalnızca {küme ayracı başlatıcılarında kötü biçimlendirildiğini }ve bunların tek eski kullanımının diziler ve POD yapıları için olduğunu unutmayın. Ayrıca, mevcut kodun ait oldukları yerde açık dökümleri varsa, kırılmaz.
aschepler

4
@j_random_hacker çalışma kağıdının dediği gibi int a = 1.0;, hala geçerlidir.
Johannes Schaub - litb

1
@litb: Teşekkürler. Aslında bunu anlaşılabilir ama hayal kırıklığı yaratan buluyorum - IMHO, C ++ 'nın başından itibaren tüm daraltıcı dönüşümler için açık sözdizimi gerektirmesi çok daha iyi olurdu.
j_random_hacker

Yanıtlar:


41

GCC'yi kullandığımda bu önemli değişiklikle karşılaştım. Derleyici, aşağıdaki gibi bir kod için bir hata yazdı:

void foo(const unsigned long long &i)
{
    unsigned int a[2] = {i & 0xFFFFFFFF, i >> 32};
}

İşlevde void foo(const long long unsigned int&):

Hata: dönüşümünü daralan (((long long unsigned int)i) & 4294967295ull)mesafede long long unsigned intiçin unsigned int} {iç

Hata: dönüşümünü daralan (((long long unsigned int)i) >> 32)mesafede long long unsigned intiçin unsigned int} {iç

Neyse ki, hata mesajları anlaşılırdı ve düzeltme basitti:

void foo(const unsigned long long &i)
{
    unsigned int a[2] = {static_cast<unsigned int>(i & 0xFFFFFFFF),
            static_cast<unsigned int>(i >> 32)};
}

Kod, bir dosyada yalnızca iki oluşum ile harici bir kitaplıktaydı. Kırılan değişikliğin çok fazla kodu etkileyeceğini sanmıyorum. Acemiler olabilir olsun , karışık olsa.


9

Son 12 yılda yazdığım herhangi bir C ++ kodunun böyle bir problemi olduğunu öğrenince kendime şaşırır ve hayal kırıklığına uğrarım. Ancak çoğu derleyici, bir şeyi kaçırmadığım sürece, derleme zamanı "daralması" hakkında baştan beri uyarılar verirdi.

Bunlar dönüşümleri de daraltıyor mu?

unsigned short b[] = { -1, INT_MAX };

Eğer öyleyse, kayan tipten integral tip örneğinize göre biraz daha sık ortaya çıkabileceklerini düşünüyorum.


1
Bunun kodda bulunması alışılmadık bir şey olmadığını neden söylediğini anlamıyorum. USHRT_MAX yerine -1 veya INT_MAX kullanmak arasındaki mantık nedir? USHRT_MAX, 2010'un sonlarında yükselişte değil miydi?

8

Karşılaştığım pratik bir örnek:

float x = 4.2; // an input argument
float a[2] = {x-0.5, x+0.5};

Sayısal değişmez bilgi örtük olarak doubleyükselmeye neden olur.


1
öyleyse floatyazarak yap 0.5f. ;)
underscore_d

2
@underscore_d floattypedef veya şablon parametresiyse (en azından hassasiyet kaybı olmadan) çalışmaz , ancak önemli olan, yazılan kodun doğru anlambilimle çalıştığı ve C ++ 11 ile bir hata haline gelmesidir. Yani, "değişimin kırılması" nın tanımı.
Jed

7

Biri aşağıdaki gibi bir şeye yakalanırsa hiç şaşırmam:

float ra[] = {0, CHAR_MAX, SHORT_MAX, INT_MAX, LONG_MAX};

(benim uygulamamda, son ikisi int / long'a dönüştürüldüğünde aynı sonucu üretmiyor, dolayısıyla daralıyor)

Yine de bunu yazdığımı hiç hatırlamıyorum. Yalnızca sınırlara bir tahmin bir şey için yararlıysa yararlıdır.

Bu da en azından belli belirsiz makul görünüyor:

void some_function(int val1, int val2) {
    float asfloat[] = {val1, val2};    // not in C++0x
    double asdouble[] = {val1, val2};  // not in C++0x
    int asint[] = {val1, val2};        // OK
    // now do something with the arrays
}

ama tamamen ikna edici değil, çünkü tam olarak iki değere sahip olduğumu biliyorsam, neden onları sırf değil de dizilere koyayım float floatval1 = val1, floatval1 = val2;? Yine de motivasyon nedir, neden derlenmeli (ve hassasiyet kaybının program için kabul edilebilir doğruluk dahilinde olması koşuluyla çalışmalıdır)float asfloat[] = {val1, val2}; olmamalı? Her iki durumda da iki ints'ten iki kayan noktayı başlatıyorum, sadece bir durumda iki kayan nokta bir kümenin üyeleri oluyor.

Bu, sabit olmayan bir ifadenin daralan bir dönüşümle sonuçlandığı durumlarda (belirli bir uygulamada), kaynak türünün tüm değerleri hedef türünde gösterilebilir ve orijinal değerlerine geri dönüştürülebilir olsa da, özellikle sert görünüyor:

char i = something();
static_assert(CHAR_BIT == 8);
double ra[] = {i}; // how is this worse than using a constant value?

Hata olmadığını varsayarsak, muhtemelen düzeltme her zaman dönüşümü açık hale getirmektir. Makrolarla tuhaf bir şey yapmadığınız sürece, bir dizi başlatıcının yalnızca dizinin türüne veya en azından türü temsil eden bir şeye yakın göründüğünü düşünüyorum, bu da bir şablon parametresine bağlı olabilir. Bu nedenle, ayrıntılı ise bir alçı kolay olmalıdır.


9
"Eğer tam olarak iki değere sahip olduğumu biliyorsam, neden onları dizilere koyarım" - örneğin, OpenGL gibi bir API gerektirdiği için.
Georg Fritzsche

5

CFLAGS'nize -Wno-daraltma eklemeyi deneyin, örneğin:

CFLAGS += -std=c++0x -Wno-narrowing

veya C ++ derleyicileri durumunda CPPFLAGS (tabii ki bu, derleme sisteminize veya Makefile'ınıza bağlıdır)
Mikolasan

4

Dönüştürme hatalarının daraltılması, örtük tamsayı yükseltme kurallarıyla kötü bir şekilde etkileşime girer.

Gibi görünen kodla ilgili bir hata yaptım

struct char_t {
    char a;
}

void function(char c, char d) {
    char_t a = { c+d };
}

Bu, daralan bir dönüştürme hatası üretir (bu, standarda göre doğrudur). Bunun nedeni ise cve dterfi olsun örtük intve elde edilenint bir başlatıcı listesinde char geri daralmış izin verilmez.

OTOH

void function(char c, char d) {
    char a = c+d;
}

tabii ki hala iyidir (aksi takdirde tüm cehennem kırılır). Ama şaşırtıcı bir şekilde

template<char c, char d>
void function() {
    char_t a = { c+d };
}

tamamdır ve c ve d'nin toplamı CHAR_MAX'tan küçükse herhangi bir uyarı olmadan derlenir. Hala bunun C ++ 11'deki bir kusur olduğunu düşünüyorum, ancak oradaki insanlar başka türlü düşünüyor - muhtemelen örtük tam sayı dönüşümünden kurtulmadan düzeltmek kolay olmadığı için (ki bu, insanlar kod yazarken geçmişten gelen bir kalıntıdır) char a=b*c/d(b * c)> CHAR_MAX) veya dönüştürme hatalarını daraltma (muhtemelen iyi bir şey) olsa bile çalışmasını ister ve isteriz.


Aşağıdakiyle karşılaştım ki bu gerçekten can sıkıcı bir saçmalık: unsigned char x; static unsigned char const m = 0x7f; ... unsigned char r = { x & m };<- dönüşümü daraltmak {}. Gerçekten mi? Öyleyse, & operatörü işaretsiz karakterleri örtük olarak int'e dönüştürür? Umurumda değil, sonucun hala imzasız bir karakter olacağı garanti, argh.
Carlo Wood

" örtük tam sayı dönüştürme " promosyonları?
curiousguy

2

Bu özellikle gerçek yaşam deneyimi, gcc'nin C ++ 03 kod tabanlarını C ++ 11'e taşımanın gerçek hayattaki ağrıları nedeniyle birçok durumda bir hatadan uyarıya dönüştüğünü gösterdiği için gerçekten de büyük bir değişiklikti. Bkz Bir gcc hata raporunda şu yorumu :

Standart yalnızca "uygun bir uygulamanın en az bir tanılama mesajı vermesini" gerektirir, bu nedenle programın bir uyarı ile derlenmesine izin verilir. Andrew'un dediği gibi, -Werror = daraltma, isterseniz bunu bir hata yapmanıza izin verir.

G ++ 4.6 bir hata verdi, ancak 4.7 için kasıtlı olarak bir uyarı olarak değiştirildi çünkü birçok kişi (ben de dahil), büyük C ++ 03 kod tabanlarını C ++ 11 olarak derlemeye çalışırken en sık karşılaşılan sorunlardan birinin daraltılması dönüşümleri buldu . Char c [] = {i, 0} gibi önceden iyi biçimlendirilmiş kod; (burada sadece char aralığında olacağım) hatalara neden oldu ve char c [] = {(char) i, 0} olarak değiştirilmek zorunda kaldı


1

Görünüşe göre GCC-4.7 artık dönüşümleri daraltmak için hata vermiyor, bunun yerine uyarı veriyor.

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.