Nullptr uintptr_t biçimine dönüştürülebilir mi? Farklı derleyiciler katılmıyor


10

Bu programı düşünün:

#include <cstdint>
using my_time_t = uintptr_t;

int main() {
    const my_time_t t = my_time_t(nullptr);
}

Msvc v19.24 ile derlenemedi:

<source>(5): error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'my_time_t'
<source>(5): note: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type
<source>(5): error C2789: 't': an object of const-qualified type must be initialized
<source>(5): note: see declaration of 't'

Compiler returned: 2

ancak clang (9.0.1) ve gcc (9.2.1) bu kodu hatasız olarak "yerler".

MSVC davranışını seviyorum, ancak standart olarak onaylandı mı? Başka bir deyişle, clang / gcc'deki hata mı yoksa bunun gcc / clang'dan gelen doğru davranış olduğunu standart olarak yorumlamak mümkün mü?


2
Bunu bir işlev stili kadrodan kopya başlatma olarak okudum. Bu daha sonra derleyici tarafından "derlenemese bile" C ++ dökümlerinden biri olarak yorumlanır . Belki de oyuncular arasında nasıl yorumlandığına dair derleyiciler arasında bir tutarsızlık vardır
wreckgar23 13

Bildiğim kadarıyla MSVC v19.24 bir C ++ 11 modunu desteklemiyor. Bunu mu demek istediniz: C ++ 14 ya da C ++ 17
ceviz

Yanıtlar:


5

Bence MSVC standartlara uygun davranmıyor.

Bu cevabı C ++ 17'ye (taslak N4659) dayandırıyorum, ancak C ++ 14 ve C ++ 11'e eşdeğer ifadeler var.

my_time_t(nullptr)bir postfix-ifadesidir ve my_time_tbir tür olduğundan ve (nullptr)parantez başlatıcısı listesinde tek bir ifade olduğundan, açık bir döküm ifadesine tam olarak eşdeğerdir. ( [expr.type.conv] / 2 )

Açık döküm özellikle birkaç farklı özel C ++ dökümünü (uzantılarla birlikte) dener reinterpret_cast. ( [Expr.cast] /4.4 ) yayınları önce güvenilir reinterpret_castolan const_castve static_cast(uzantılı ve ayrıca kombinasyon halinde), ancak bunların hiçbiri dökme olabilir std::nullptr_ttamamlayıcı tipine.

Ancak reinterpret_cast<my_time_t>(nullptr)başarılı olmalıdır, çünkü [expr.reinterpret.cast] / 4 , bir tür değerinin, bütün işaretçi değerlerini temsil edecek kadar büyük bir tür olması gerektiğinden , std::nullptr_tböyle bir integral türüne dönüştürülebileceğini söylüyor . aynı standart paragraf, integral türüne dönüştürmeye izin verir .reinterpret_cast<my_time_t>((void*)0)my_time_t = std::uintptr_tvoid*

İşlevsel gösterim yerine döküm gösterim kullanıldığında MSVC'nin dönüştürmeye izin vermesi özellikle gariptir:

const my_time_t t = (my_time_t)nullptr;

1
Evet. Not o static_castözellikle tuzak C tarzı dökme merdiven amaçlanan bazı durumlarda vardır (örneğin, belirsiz bir tabana C tarzı dökme bir kötü şekillendirilmiş olan static_castbir yerine reinterpret_cast), ama hiçbiri burada uygulanır.
TC

my_time_t(nullptr)tanımı gereği aynıdır (my_time_t)nullptr, dolayısıyla MSVC birini kabul etmek ve diğerini reddetmek kesinlikle yanlıştır.
Richard Smith

2

Bu Çalışma Taslağı C ++ Standardında (2014'ten) ayrılmaz bir türe dönüştürmenin yasak olduğu konusunda açık bir söz bulamamam da, böyle bir dönüşüme izin verildiğini de belirtmiyorum!std::nullptr_t

Ancak, std::nullptr_tila arasında dönüşüm bool durumu açıkça belirtilmiştir:

4.12 Boole dönüşümleri
Aritmetik, kodlanmamış numaralandırma, işaretçi ya da işaretçi ile üye türünün bir ön değeri, bool türünün bir ön değerine dönüştürülebilir. Sıfır değeri, null işaretçi değeri veya null üye işaretçi değeri false değerine dönüştürülür; diğer herhangi bir değer true değerine dönüştürülür. Doğrudan başlatma (8.5) için, std :: nullptr_t türünde bir ön değer, bool türünde bir ön değere dönüştürülebilir; elde edilen değer yanlıştır.

Ayrıca, bu taslak belgede , integral tipine dönüştürmenin geçtiği tek yer std::nullptr_t"reinterpret_cast" bölümünde yer almaktadır:

5.2.10 Dökümü yeniden yorumlayın
...
(4) Bir işaretçi, açıkça tutacak kadar büyük tümleşik tiplere dönüştürülebilir. Eşleme işlevi uygulama tanımlıdır. [Not: Altta yatan makinenin adresleme yapısını bilenler için şaşırtıcı değildir. - end note] std :: nullptr_t türünde bir değer bir integral türüne dönüştürülebilir; dönüşüm, (void *) 0'ın integral türüne dönüştürülmesi ile aynı anlama ve geçerliliğe sahiptir. [Not: reinterpret_cast, herhangi bir türdeki değeri std :: nullptr_t türüne dönüştürmek için kullanılamaz. - son not]

Bu nedenle, bu iki gözlemden biri , derleyicinin doğru olduğunu makul olarak tahmin edebilir (IMHO) MSVC.

DÜZENLEME : Ancak, "işlevsel gösterim" kullanımınız aslında tam tersi olabilir! MSVCDerleyici örneğin, C tarzı döküm kullanarak bir sorunu yok:

uintptr_t answer = (uintptr_t)(nullptr);

ancak (kodunuzda olduğu gibi), bundan şikayet eder:

uintptr_t answer = uintptr_t(nullptr); // error C2440: '<function-style-cast>': cannot convert from 'nullptr' to 'uintptr_t'

Yine de, aynı Taslak Standarttan:

5.2.3 Açık tür dönüşümü (işlevsel gösterim)
(1) Basit tür belirteci (7.1.6.2) veya tür adı belirleyicisi (14.6) ve ardından parantez içine alınmış bir ifade listesi, ifade listesi verildiğinde belirtilen türde bir değer oluşturur. İfade listesi tek bir ifadeyse, tür dönüştürme ifadesi karşılık gelen döküm ifadesine (5.4) eşdeğerdir (tanım olarak ve anlam olarak tanımlanmışsa). ...

"Karşılık gelen döküm ifadesi (5.4)", C tarzı bir döküm anlamına gelebilir.


0

Hepsi standart uyumludur (C ++ için ref. N4659 taslağı).

nullptr [lex.nullptr] içinde şu şekilde tanımlanır:

İşaretçi değişmezi nullptr anahtar kelimesidir. Std :: nullptr_t türünün bir önkoşuludur. [Not: ..., bu tür bir ön değer boş bir işaretçi sabitidir ve boş bir işaretçi değerine veya boş üye işaretçi değerine dönüştürülebilir.]

Notlar normatif olmasa bile, bu standart nullptriçin boş bir işaretçi değerine dönüştürülmesi beklenir .

Daha sonra [conv.ptr] 'da bulduk:

Boş bir işaretçi sabiti, sıfır değerine sahip bir tamsayı ya da std :: nullptr_t türünün bir önkoşuludur. Bir boş işaretçi sabiti bir işaretçi türüne dönüştürülebilir; .... integral türünde bir boş gösterici sabiti std :: nullptr_t türünde bir ön değere dönüştürülebilir.

Burada yine neler standardın gerektirdiği olmasıdır 0bir dönüştürülebilir std::nullptr_tve bu nullptrherhangi bir işaretçi türüne dönüştürülebilir.

Benim okuma standardı konusunda hiçbir gereksinimi olmasıdır nullptredilebilir doğrudan ayrılmaz bir türü ya da değil dönüştürülür. Bu noktadan itibaren:

  • MSVC'nin katı bir okuması var ve dönüşümü yasaklıyor
  • Clang ve gcc, bir ara void *dönüşüm gerçekleşmiş gibi davranır .

1
Bence bu yanlış. Bazı boş işaretçi sabitleri, sıfır değerine sahip tamsayı değişmezleridir, ancak nullptrtümleşik olmayan bir türe sahip olması nedeniyle değildir std::nullptr_t. 0 bir std::nullptr_tdeğere dönüştürülebilir ancak değişmez değere dönüştürülemez nullptr. Bunların hepsi kasıtlıdır, std::nullptr_tistenmeyen dönüşümleri önlemek için daha kısıtlı bir türdür.
MSalters

@MSalters: Haklı olduğunu düşünüyorum. Yeniden yazmak istedim ve yanlış yaptım. Yayınınızı yorumunuzla birlikte düzenledim. Yardımın için teşekkürler.
Serge Ballesta
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.