2016-12-12 15 views
29

En şaşırtıcı hatalarını keşfettim ve derleyicinin neden benim için işaretlemediğini anlamıyorum. Aşağıdaki yazarsanız:Neden bir alt çağrı sonucu atamak için bir derleyici hatası değil?

string s = "abcdefghijkl"; 
cout << s << endl; 

s.substr(2,3) = "foo"; 
s.substr(8,1) = '.'; 
s.substr(9,1) = 4; 
cout << s << endl; 

derleyici olursa olsun hiçbir sorunu vardır ve atama ifadeleri çıktısı ne dayanarak, hiçbir etkisi görülmektedir. Buna karşılık,

s.front() = 'x'; 

yatan kiriş değişimi ( front bir karakter bir başvuru verir beri) ben beklediğiniz etkiye sahiptir ve
s.length() = 4; 

da bir derleyici üreten beklenen etkiye sahiptir length bir tamsayı döndürdüğü için bir değer olmayan bir şeye atayamayacağınız için hata oluştu. (Peki, bir size_t yine de.)

Peki ... neden derleyici, substr aramasının sonucuna atama konusunda şikayet etmiyor? Bir dize değeri döndürür, bir referans değil, bu yüzden atanabilir olmamalı, değil mi? Ama bunu g++ (6.2.1) ve clang++ (3.9.0) 'da denedim, bu yüzden bir hata gibi görünmüyor ve ayrıca C++ sürümüne duyarlı görünmüyor (03, 11'i denedim) 14). koda

+8

'Bir dize değeri döndürür, bir başvuru değil, bu yüzden atanamaz olmamalıdır Neden? Neden böyle düşünüyorsunuz? Str; str = "foo"; 'aynısını mı? – DeiDei

+2

Elbette ama dize dizisinde; str = "foo"; ', 'str' değişkeni açıkça bir değerdir. Geçici değerler ... değil mi? Demek istediğim, int n; n = 4; 'ayrıca gayet iyi çalışıyor, ama benim üçüncü örneğim hatalar (beklediğim gibi). – blahedo

+2

Evet, bir lvalue olmak için bir isim verilmedikçe, aşağıdaki satırda biten bir ömür boyu olan bir 'xvalue'. Fakat xvalues ​​de değiştirilebilir olmalı. Bunu göz önünde bulundurun: cout << s.substr (2,8) .push_back ('x'); 'push_back' öğesini geçici olarak yazdırıp yazdırılmasını sağlar. Bir sonraki satırda yok edildi. – DeiDei

cevap

45

substr() sonucu std::string geçici bir nesnedir; bu, özgün dizede bir görünüm değil, alt dizinin bağımsız bir kopyasıdır.

std::string nesnesi olmak üzere, bir atama işleç işlevi vardır ve kodunuz, geçici nesneyi değiştirmek için bu işlevi çağırır.

  1. geri const nesnesi: -

    Bu biraz şaşırtıcı insanlar durumu iyileştirmek için denemek iki yol vardır genelde bu yüzden geçici bir nesne değiştirerek ve genellikle sonuç atarak, bir mantık hatasını gösterir .

  2. Atama işlecindeki lvalue ref-niteleyiciyi kullanın.

Seçenek 1 kodunuz için derleme hatasına neden olur, ama aynı zamanda bazı geçerli kullanım-durumlarda kısıtlar (örn move dönüş değeri dışında -ing - Bir const dize dışına hareket edemez).

Seçenek 2, sol taraf bir lvalue olmadığı sürece atama operatörünün kullanılmasını engeller. Hepsi kabul etmese de bu iyi bir fikir IMHO'dur; see this thread for discussion.

Her durumda; C++ 11 it was proposed'a geri-niteleyiciler eklendiğinde ve tüm kapların belirtimini C++ 03'ten değiştirdikten sonra, bu teklif kabul edilmedi (muhtemelen mevcut kodu kırması durumunda).

std::string 1990'larda tasarlandı ve bugünlerde arka planda kötü görünen bazı tasarım seçimleri yaptı, ama biz buna bağlıyız. Sorunu sadece std::string'un kendisinde anlamak zorunda kalacaksınız ve belki de ref-niteleyiciler, veya görüşler veya herhangi bir şey kullanarak kendi sınıflarınızda bunu önlemek zorunda kalacaksınız.

7

Görünüş:

s.substr(2,3) = "foo"; 

işlev çağrısı substr döner bir dize, bir nesnenin ve geçici bir değerdir. Bundan sonra, bu nesneyi değiştirirsiniz (aslında, std::string sınıfından aşırı yüklenme operatörünü çağırarak). Bu geçici nesne herhangi bir şekilde kaydedilmez. Derleyici, bu değiştirilmiş geçici durumu ortadan kaldırır.

Bu kod bir anlam ifade etmemektedir. Derleyici neden uyarı vermiyor diye sorabilirsiniz. Cevap, derleyicinin daha iyi olabileceğidir. Derleyiciler insanlar tarafından yazılmıştır, tanrı değil. Ne yazık ki C++, tanımlanmamış kod veya kod tanımlanmamış davranışları tetikleyen çeşitli yollardan tonlarca izin verir. Bu, bu dilin yönlerinden biridir. Diğer birçok dile kıyasla programcıdan daha iyi bilgi ve daha yüksek dikkat gerektirir.

Sadece MSVC 2015 kodu ile kontrol:

std::string s1 = "abcdef"; 
s1.substr(1, 2) = "5678"; 

iyi derler.

+0

Her halükarda, derleyicinin böyle bir şey için montaj üretmeyi düşünmediğini eklemesi güvenlidir, çünkü hiçbir şey yapmaz. – DeiDei

+0

Kabul ediyor. Her ne kadar bu bir optimizasyon meselesidir. Optimizasyon kapatılmış olabilir ve belirli optimizasyonun gerçekleşeceğini garanti etmek mümkün değildir. Büyük olasılıkla ** olacak ** ama iyi değilse, derleyici hala iyi olurdu. –

+0

Bu, soruma yanıt vermiyor.Kodun neden bir şey yapmadığını anlıyorum. Anlamıyorum neden * derler *. – blahedo

12

Kodunuzun derlenmesi nedeni, yasal C++ olmasıdır. İşte neler olduğunu açıklayan bir bağlantı.

https://accu.org/index.php/journals/227

Oldukça uzun, bu yüzden en alakalı bölümünü teklif edeceğiz:

Dışı sınıf SağDeğerler değiştirilebilir değil, ne de cv nitelikli türlerini (cv-nitelikleri olabilir göz ardı edilir). Tersine, sınıf rvalues ​​değiştirilebilir ve üye işlevleri aracılığıyla bir nesneyi değiştirmek için kullanılabilir. Ayrıca, cv nitelikli türlere de sahip olabilirler.Bir sınıfın bir örneği olmayan ve kendisine bir örnek olduğu için std::string::substr dönen rvalue atayabileceğin sebebi,

Yani std::string::length tarafından döndürülen rvalue atayamayacağı nedenidir bir sınıfın

Dilin neden bu şekilde tanımlandığını anlamıyorum, ama böyle.

İlgili konular