Noreturn'un anlamı nedir?


189

[dcl.attr.noreturn] aşağıdaki örneği sağlar:

[[ noreturn ]] void f() {
    throw "error";
    // OK
}

ama amacının ne olduğunu anlamıyorum [[noreturn]], çünkü işlevin dönüş tipi zaten void.

Peki, özelliğin amacı noreturnnedir? Nasıl Kullanılır?


1
Böyle bir ilgiyi hak eden bu tür bir funciton (büyük olasılıkla bir program yürütmesinde bir kez olacak) ne kadar önemlidir? Bu kolayca tespit edilebilen bir durum değil mi?
user666412

1
@MrLister OP, “dönüş” ve “dönüş değeri” kavramlarını ele alıyor. Neredeyse hep birlikte nasıl kullanıldıkları göz önüne alındığında, karışıklığın haklı olduğunu düşünüyorum.
Slipp D.Thompson

Yanıtlar:


209

Noreturn özniteliğinin, çağırana dönmeyen işlevler için kullanılması gerekiyordu. Bu, geçersiz işlevler anlamına gelir (arayana geri döner - sadece bir değer döndürmezler), ancak işlev bittikten sonra kontrol akışının çağrı işlevine geri dönmeyeceği işlevler (örn. Uygulamadan çıkan işlevler, sonsuza kadar döngü veya örneğinizde olduğu gibi istisnalar atın).

Bu, derleyiciler tarafından bazı optimizasyonlar yapmak ve daha iyi uyarılar oluşturmak için kullanılabilir. Örneğin f, noreturn özniteliği varsa , derleyici yazarken g()ölü kod olduğu konusunda sizi uyarabilir f(); g();. Benzer şekilde derleyici, çağrılardan sonra eksik iade ifadeleri konusunda sizi uyarmamayı bilir f().


5
Bir işlevi hakkında ne gibi execveo olmamalı dönmek fakat olabilir ? Noreturn niteliğine sahip olmalı mı ?
Kalrish

22
Hayır, olmamalıdır - kontrol akışının arayana geri dönme olasılığı varsa, noreturnözelliğe sahip olmamalıdır . noreturnvb iptal örneğin size çıkış (call çünkü), (), assert (0) - sizin işlev arayana dönebilir kontrol akışının önce programı sonlandırır bir şey yapmak için garanti yalnızca kullanılabilir
RavuAlHemio

6
@ SlippD.Thompson Bir noreturn işlevine yapılan bir çağrı bir try-block'a sarılırsa, catch bloğundaki herhangi bir kod tekrar erişilebilir olarak sayılır.
sepp2k

2
@ sepp2k Havalı. Yani geri dönmek imkansız değil, sadece anormal. Bu yararlı. Şerefe.
Slipp D.Thompson

7
@ SlippD.Thompson hayır, geri dönmek imkansız. Bir istisna atmak geri gelmez, bu yüzden her yol atılırsa o zaman noreturn. Bu istisnayı işlemek, döndürdüğü durumla aynı değildir. Çağrıdan sonraki kodlara tryhala erişilemez ve eğer değilse void, geri dönüş değerinin tahsisi veya kullanımı gerçekleşmez.
Jon Hanna

63

noreturnderleyiciye işlevin herhangi bir değer döndürmediğini söylemez. Derleyiciye kontrol akışının arayana geri dönmeyeceğini bildirir . Bu, derleyicinin çeşitli optimizasyonlar yapmasına izin verir - çağrı çevresindeki herhangi bir geçici durumu kaydetmesi ve geri yüklemesi gerekmez, aksi takdirde çağrıyı takip edecek herhangi bir kodu ortadan kaldırabilir, vb.


29

Bu, işlevin tamamlanmayacağı anlamına gelir. Kontrol akışı şu çağrıdan sonra hiçbir zaman ifadeye ulaşmaz f():

void g() {
   f();
   // unreachable:
   std::cout << "No! That's impossible" << std::endl;
}

Bilgiler, derleyici / iyileştirici tarafından farklı şekillerde kullanılabilir. Derleyici, yukarıdaki kodun erişilemez olduğuna dair bir uyarı ekleyebilir ve asıl kodunu g()farklı yollarla örneğin süreklilikleri desteklemek için değiştirebilir.


3
gcc / clang uyarı vermiyor
TemplateRex

4
@TemplateRex: Derleyin -Wno-returnve bir uyarı alacaksınız. Muhtemelen beklediğiniz gibi değil ama muhtemelen derleyicinin ne [[noreturn]]olduğunu bildiğini ve bundan faydalanabileceğini söylemek yeterli . (Ben -Wunreachable-codetekme değildi biraz şaşırdım ...)
David Rodríguez - dribeas

3
@TemplateRex: Üzgünüz -Wmissing-noreturn, uyarı, akış analizinin std::coutulaşılamayacağını belirlediğini ima ediyor . Ben montaj oluşturulan bakmak için el altında yeni yeterince gcc yok, ancak arama için eğer sürpriz olmaz operator<<düştü
David Rodríguez - dribeas

1
İşte bir montaj dökümü (-S -o - bayraklar coliru), gerçekten "ulaşılamaz" kodunu düşürür. İlginçtir ki, ulaşılamayan kodu ipucu olmadan bırakmak -O1zaten yeterlidir[[noreturn]] .
TemplateRex

2
@TemplateRex: Tüm kod aynı çeviri birimindedir ve görünürdür, böylece derleyici [[noreturn]]koddan çıkarım yapabilir . Bu çeviri biriminde yalnızca başka bir yerde tanımlanmış işlev bildirimi varsa, derleyici işlevin geri dönmediğini bilmediği için bu kodu bırakamaz. Özniteliğin derleyiciye yardım etmesi gereken yer burasıdır.
David Rodríguez - dribeas

17

Önceki cevaplar noreturn'un ne olduğunu doğru bir şekilde açıkladı, ama neden var olduğunu değil. Ben "optimizasyon" yorum ana amacı olduğunu sanmıyorum: dönmeyen fonksiyonlar nadirdir ve genellikle optimize edilmesi gerekmez. Daha ziyade, noreturnun ana nedeninin yanlış pozitif uyarılardan kaçınmak olduğunu düşünüyorum. Örneğin, şu kodu göz önünde bulundurun:

int f(bool b){
    if (b) {
        return 7;
    } else {
        abort();
    }
 }

Abort () "noreturn" olarak işaretlenmemişse, derleyici bu kodun f'nin beklendiği gibi bir tamsayı döndürmediği bir yola sahip olduğu konusunda uyarmış olabilir. Ancak abort () döndürülmediği için kodun doğru olduğunu bilir.


Listelenen diğer tüm örnekler geçersiz işlevleri kullanır - hem [[dönüş yok]] yönergesine hem de geçersiz olmayan dönüş türüne sahip olduğunuzda nasıl çalışır? [[Dönüş yok]] direktifi sadece derleyici geri dönmeme ve uyarıyı göz ardı etme olasılığı konusunda uyarmaya hazır olduğunda mı devreye girer? Örneğin, derleyici gider: "Tamam, işte geçersiz bir işlev." * derlemeye devam ediyor * "Oh lanet olsun, bu kod geri dönmeyebilir! Kullanıcıyı uyarmalı mıyım? Devam et "
Raleigh L.

2
Örneğimdeki noreturn işlevi f () değil, abort (). Geçersiz olmayan bir işlev noreturn'u işaretlemek mantıklı değildir. Bazen bir değer döndüren ve bazen dönen bir işlev (iyi bir örnek execve () şeklindedir) noreturn olarak işaretlenemez.
Nadav Har'El


11

Teorik olarak konuşulan tip, voiddiğer dillerde unitya da olarak adlandırılan şeydir top. Mantıksal eşdeğeri Doğru'dur . Herhangi bir değer yasal olarak kullanılabilir void(her tür bir alt tipidir void). Bunu "evren" seti olarak düşünün; dünyadaki tüm değerlerle ortak bir işlem yoktur , bu nedenle bir tür değerinde geçerli bir işlem yoktur void. Başka bir deyişle, bir şeyin evren setine ait olduğunu söyleyerek size hiçbir bilgi vermez - zaten biliyorsunuz. Yani şu ses:

(void)5;
(void)foo(17); // whatever foo(17) does

Ancak aşağıdaki görev değil:

void raise();
void f(int y) {
    int x = y!=0 ? 100/y : raise(); // raise() returns void, so what should x be?
    cout << x << endl;
}

[[noreturn]]Öte yandan, kimi kez adlandırıldığı empty, Nothing, Bottomya da Botve mantıksal eşdeğeridir FALSE . Hiç değeri yoktur ve bu tipteki bir ifade herhangi bir türe çevrilebilir (yani alt tipidir). Bu boş kümedir. Birisi size "foo () ifadesinin değerinin boş kümeye ait olduğunu" söylerse , son derece bilgilendirici olduğunu - bu ifadenin asla normal yürütülmesini tamamlamayacağını söyler; iptal edecek, fırlatacak ya da asacaktır. Tam tersidir void.

Bu yüzden aşağıdakiler mantıklı değil (pseudo-C ++, noreturnbirinci sınıf C ++ tipi olmadığından)

void foo();
(noreturn)5; // obviously a lie; the expression 5 does "return"
(noreturn)foo(); // foo() returns void, and therefore returns

Ancak aşağıdaki atama tamamen meşrudur, çünkü throwderleyici tarafından geri dönmeyeceği anlaşılmaktadır:

void f(int y) {
    int x = y!=0 ? 100/y : throw exception();
    cout << x << endl;
}

Mükemmel bir dünyada, yukarıdaki noreturnişlev için dönüş değeri olarak kullanabilirsiniz raise():

noreturn raise() { throw exception(); }
...
int x = y!=0 ? 100/y : raise();

Ne yazık ki C ++, muhtemelen pratik nedenlerle izin vermez. Bunun yerine, [[ noreturn ]]derleyici optimizasyonlarına ve uyarılarına rehberlik etmeye yardımcı olan özelliği kullanma olanağı sağlar .


5
Hiçbir şey için dökme olabilir voidve voidsonucunu asla trueveya falsebaşka ya da bir şey.
Daha Net

5
Dediğim zaman true, " truetipin değeri" demek booldeğil, mantık duygusu, bkz. Curry-Howard yazışmaları
Elazar

8
Belirli bir dilin tip sistemine uymayan soyut tip teorisi, o dilin tip sistemini tartışırken önemsizdir. Söz konusu :-) sorusu, tip teorisi ile değil C ++ ile ilgilidir.
Daha Net

8
(void)true;cevabından da anlaşılacağı gibi, tamamen geçerlidir. void(true)sözdizimsel olarak tamamen farklı bir şeydir. Bir yapıcıyı argüman olarak voidçağırarak yeni bir tür nesne yaratma girişimidir true; Bu, diğer nedenlerin yanı sıra başarısız olur, çünkü voidbirinci sınıf değildir.
Elazar

2
İşte bu. C tarzı döküm değil, tip (değer) döküm kullanmaya alışkınım.
Daha Net
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.