Boş gösterici argümanı ve imkansız son koşullar ile standart istisnalar oluşturun


9

Aşağıdaki programı düşünün:

#include<stdexcept>
#include<iostream>

int main() {
    try {
        throw std::range_error(nullptr);
    } catch(const std::range_error&) {
        std::cout << "Caught!\n";
    }
}

GCC ve Clang ile libstdc ++ çağrı std::terminateve mesaj ile programı iptal

terminate called after throwing an instance of 'std::logic_error'
  what():  basic_string::_S_construct null not valid

İstisna yapımında libc ++ segfaults ile clang.

Bkz Godbolt .

Derleyiciler standart uyumlu mudur? Standart [diagnostics.range.error] (C ++ 17 N4659) ' un ilgili bölümü, aşırı yüklenmeye göre tercih edilmesi gereken std::range_errorbir const char*yapıcı aşırı yüklenmesine sahip olduğunu söylüyor const std::string&. Bölüm ayrıca yapıcı üzerinde herhangi bir ön koşulu belirtmez ve yalnızca son koşulu belirtir

Hedefşartlar : strcmp(what(), what_­arg) == 0.

what_argBir boş gösterici ise bu son koşul her zaman tanımsız davranışa sahiptir , bu da programımın tanımlanmamış davranışa sahip olduğu ve her iki derleyicinin de uygun davrantığı anlamına mı geliyor? Değilse, standartta böyle imkansız postconditions nasıl okunmalıdır?


İkinci düşüncemde, bunun benim programım için tanımlanmamış davranış anlamına gelmesi gerektiğini düşünüyorum, çünkü eğer o zaman (geçerli) null sonlu dizelere işaret etmeyen işaretçiler de izin verilecekti, bu açıkça anlamsızdı.

Dolayısıyla, bunun doğru olduğunu varsayarak, soruna standardın bu tanımlanmamış davranışı nasıl ima ettiği üzerine daha fazla odaklanmak istiyorum. Çağrının tanımlanmamış davranışa sahip olması veya önkoşulun basitçe unutulması mıdır?


Bu sorudan ilham alındı .


Görünüşe göre std :: range_error öğelerini referans olarak saklamasına izin verilir, bu yüzden şaşırmazdım. Arama what()sırasında nullptrmuhtemelen sorunlara yol açacağını geçirilir.
Chipster

@Chipster Tam olarak ne demek istediğinizden emin değilim, ama bunu tekrar düşündükten sonra, tanımsız davranış olması gerektiğini düşünüyorum. Sorumumu standart ifadenin tanımsız davranışları nasıl ifade ettiğine daha fazla odaklanmak için düzenledim .
Ceviz

Eğer geçilirse, bunun değeri elde etmek için bir noktada dereference olması nullptrgerektiğini düşünürüm what(). Bu nullptr, en iyi sorunlu olan ve çökmesi kesin olan bir kayıt dışı bırakma olacaktır.
Chipster

Yine de katılıyorum. Tanımlanmamış bir davranış olmalıdır. Ancak, bunu neden becerilerimin ötesinde olduğunu açıklayan yeterli bir cevaba koymak.
Chipster

Ben strcmpdeğerini tanımlamak için kullanılan beri argümanın geçerli bir C dize işaret bir önkoşul olduğunu düşünüyorum what_arg. C standardından ilgili bölümün yine de belirttiği şey, bu spesifikasyon tarafından belirtilmektedir <cstring>. Tabii ki ifadeler daha açık olabilir.
LF

Yanıtlar:


0

Gönderen doc :

Std :: range_error kopyalamanın istisnalar atmasına izin verilmediğinden, bu ileti genellikle dahili olarak ayrı olarak ayrılmış başvuru sayılan dize olarak depolanır. Bu yüzden de std :: string && alan bir kurucu yoktur, içeriği yine de kopyalamak zorunda kalacaktır.

Bu neden segfault aldığınızı gösterir, api gerçekten gerçek bir dize gibi davranır. Genel olarak cpp'de bir şey isteğe bağlıysa, ihtiyaç duymadığı şeyi almayan aşırı yüklenmiş bir yapıcı / işlev olacaktır. Bu nedenle nullptr, isteğe bağlı bir şeyi belgelemeyen bir işleve geçmek tanımsız davranış olacaktır. Genellikle API'lar C dizeleri hariç istisnalar almaz. Bu nedenle IMHO, nullptr değerini a const char *, tanımlanamayan bir davranış bekleyen bir işlev için kabul etmeyi güvenlidir . std::string_viewBu durumlar için daha yeni API'ler tercih edilebilir .

Güncelleme:

Genellikle NULL kabul etmek için bir işaretçi alarak bir C ++ API varsaymak adil. Ancak C dizeleri özel bir durumdur. Kadar std::string_view, bunları verimli geçmesi için daha iyi bir yolu yoktu. Genel olarak bir API kabulü için const char *, geçerli bir C dizesi olması gerektiği varsayımı olmalıdır. yani, char'\ 0' ile sona eren s dizisine bir işaretçi .

range_errorişaretçinin nullptrolmadığını doğrulayabilir, ancak '\ 0' ile sonlanırsa doğrulayamaz. Yani herhangi bir doğrulama yapmasa iyi olur.

Standarttaki tam ifadeyi bilmiyorum, ancak bu ön koşul muhtemelen otomatik olarak kabul edilir.


-2

Bu temel soru geri gider nullptr bir std :: dize oluşturmak için Tamam mı? ve ne yapmalı?

www.cplusplus.com diyor

S bir boş gösterici ise, n == npos ise veya [first, last) tarafından belirtilen aralık geçerli değilse, tanımlanmamış davranışa neden olur.

Öyleyse ne zaman

throw std::range_error(nullptr);

uygulama gibi bir şey yapmaya çalışır denir

store = std::make_shared<std::string>(nullptr);

tanımsız. Hangi (standart gerçek ifadeleri okumak zorunda kalmadan) bir hata düşünecektim. Bunun yerine özgürlük geliştiricileri

if (what_arg)
  store = std::make_shared<std::string>(nullptr);

Ama sonra alıcı nullptr'i kontrol etmeli what();ya da orada çökecektir . Bu nedenle, std::range_errordiğer bazı diller gibi boş bir dize veya "(nullptr)" atamalıdır.


“Bu temel soruya geri dönüyor, nullptr'den bir std :: string oluşturmak uygun mudur?” - Öyle sanmıyorum.
Konrad Rudolph

Ben standart istisna bir kaydetmek zorunda herhangi bir yerde belirtir sanmıyorum std::stringve std::stringyapıcı aşırı yük çözünürlüğü ile seçilmelidir.
ceviz

const char *Aşırı yük aşırı yük kararıyla seçilir
MM
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.