`String.assign (string.data (), 5)` iyi tanımlanmış mı yoksa UB mi?


11

Bir iş arkadaşı şunu yazmak istedi:

std::string_view strip_whitespace(std::string_view sv);

std::string line = "hello  ";
line = strip_whitespace(line);

Ben dönen söyledi string_viewbeni huzursuz yapılan önsel , ve dahası, örtüşme burada bana UB benziyordu.

line = strip_whitespace(line)Bu durumda eşdeğer olduğunu kesin olarak söyleyebilirim line = std::string_view(line.data(), 5). Buna string::operator=(const T&) [with T=string_view]eşdeğer olarak line.assign(const T&) [with T=string_view]tanımlanan, eşdeğer olarak line.assign(line.data(), 5)tanımlanan, bunu yapmak için tanımlanan çağrı yapacağına inanıyorum :

Preconditions: [s, s + n) is a valid range.
Effects: Replaces the string controlled by *this with a copy of the range [s, s + n).
Returns: *this.

Ancak bu, takma ad olduğunda ne olacağını söylemez.

Bu soruyu dün cpplang Slack'de sordum ve karışık cevaplar aldım. Burada süper yetkili yanıtlar ve / veya gerçek kütüphane satıcılarının uygulamalarının ampirik analizi aranıyor.


Ben test durumları yazdım için string::assign, vector::assign, deque::assign, list::assign, ve forward_list::assign.

  • Libc ++ bu test senaryolarının tümünü çalışır hale getirir.
  • Libstdc ++ forward_list, hangi segfaultlar hariç hepsini çalıştırır .
  • MSVC'nin kütüphanesini bilmiyorum.

Libstdc ++ içindeki segfault bana bunun UB olduğunu umut ediyor; ancak hem libc ++ hem de libstdc ++ 'ın en azından yaygın durumlarda bu işi yapmak için büyük çaba göstereceğini görüyorum.


Test vakalarını ASan ile derlediniz ve / veya Valgrind altında çalıştırdınız mı? Bu, kodun erişim ihlallerine neden olup olmadığını tahmin eder, ancak tanımdan ziyade pratikte hala işe yarayabilir.
Konrad Rudolph

1
"Basic_string öğesinin herhangi bir üye işlevi veya işleci bir istisna atarsa, bu işlevin veya işlecin basic_string nesnesi üzerinde başka bir etkisi olmaz." - bu, depolama alanı tahsisini mevcut depolama alanı serbest bırakılmadan önce gerçekleşmeye zorlar, böylece tahsis başarısız olursa değiştirmeden bir istisna atılır *this. Ancak, mevcut depolamanın yeniden kullanılmasını engelleyecek bir şey görmüyorum, bu durumda depolama alanı kopyalamanın anlambilimi belirtilmediğinden, bu belirtilmez.
Sam Varshavchik


2
Bahsedilen sekans kapları için, [tab: container.seq.req] ' deki assignkoşulların ön koşul ihlali nedeniyle kesinlikle UB'dir .
ceviz

Yanıtlar:


8

Sizin olmayan bir istisna birkaç engelleme assign, bir dize üzerinde const üyesi olmayan bir işlev (yani ) çağırmak öğeleri [...] işaretçileri [...] geçersiz kılar . Bu ihlal önkoşul üzerinde assignolduğu [s, s + n)bu tanımsız davranıştır böylece, geçerli bir aralıktır.

string::operator=(string const&)Özellikle kendiliğinden atamayı kolaylaştırmak için bir dilin olduğunu unutmayın .


1
Peki, geçersiz kılma noktası ve ön koşulun tutulması gereken nokta tam olarak nedir? Cevap, üye işlevi çağrıldıktan sonra önkoşulun geçerli olması gerektiği varsayımıdır.
ceviz

1
@walnut ben hiçbir dil-avukat (ne bir özellikle genişletilmiş C ++ bilgisi olan kişi), ama biz sizin senaryo ters, bir soru sorabilir zaman - aralığı geçersiz olabilir sırasında yürütülmesi assign? Evet, o zaman belirli bir noktayı ayarlamak zorunda kalacak olursa işaretine ata uygulanmasına tam olarak ne zaman geçersiz kılma oluşabilir ve bunun ++ yapacağı bir şey C değil inanıyoruz. Yine de yanılmış olabilirim.
Fureeish

2
@Fureeish Ben de bilmiyorum, ama bkz. LWG 526 numaralı " bir kusur değil " olarak kapandı , bu da vektörün kendisinde std::vector::insert(iterator pos, const T& value)olması durumunda çalışması gereken kapatma önerisinde bahsediyor value, çünkü standart bunu belirtmiyor bu referans çağrı tarafından geçersiz kılınmış olsa bile, çalışmamasına izin verilir.
ceviz

1
@walnut " çalışmak için gereklidir, çünkü standart çalışmaması için izin vermez. " - seviyorum . Sooo ... pratikte neler olduğunu sormaya değer mi? Böyle bir durumda uygulama , argümanın bir kopyasını oluşturmak için gerekli mi? Gerçekçi olarak nasıl uygulayabilirsiniz ..? Derleyicilerin imkansızı yapmasını gerektiren standart duydum - bu durumlardan biri mi? Ne olursa olsun, yorum için teşekkür ederim!
Fureeish

1
@Fureeish Aslında önceki (şimdi silinmiş) örneğim aslında test etmek istediklerimi test etmiyordu. Burada hem libc ++ hem de libstdc ++ 'nin gerektiğinde yeniden tahsis işlemine geçmeden önce kopya yaptığını gösteren sabit bir örnek.
ceviz
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.