Soru Üçlü operatörden bir istisna atılabilir mi?


Bazen, sadece bir ifadenin (bir constexpr). Bir koşulun kontrol edilmesi gerekiyorsa ve sadece bir ifadeye izin verilirse, koşullu operatör tek seçenektir. Bir hata durumunda, koşullu operatörden bir istisna atmak güzel olurdu, örn .:.

template <typename It>
typename std::iterator_traits<It>::reference
access(It it, It end) {
    return it == end? throw std::runtime_error("no element"): *it;
}

Bununla birlikte, yukarıdaki işlev, aşağıdaki gibi kullanıldığında derlenmez:canlı örnek):

std::vector<int> v;
access(v.begin(), v.end());

Derleyici, olmayan birconst geçici referans. Derleyici hakkında şikayet etmez throwAncak, başına -express. Öyleyse: Koşullar istisnai operatörden atılabilir ve eğer öyleyse, yukarıdaki kodda neyin ters gittiği olabilir?


23
2017-12-30 21:49


Menşei


Dönüş türünde bir değişiklik ile derlenir: ideone.com/pjOkh8. - stefan
@stefan: Fonksiyonu değere göre döndürürseniz o zaman kopyaArtık aynı işlev değil! - David Rodríguez - dribeas
@ DavidRodríguez-dribeas true, ancak derleme hatasını ortadan kaldırır ve en azından bu derleyicinin istisnai durumlar dışında üçüncül operatörde boğulmadığını ortaya çıkarır. - stefan
Muhtemelen bir kusurdur. CWG 1560 ve CWG 1550 - dyp
C ++ 14 ile, üçlü operatörün önemini yitirdiğini unutmayın. constexpr (Clang 3.4'ten itibaren tam olarak desteklenir, ör. coliru.stacked-crooked.com). İçerdeki istisnalar atılıyor constexpr virgülle kombine edilmiş gibi görünüyor hataları işlemek için deyimsel yol. - TemplateRex


Cevaplar:


Koşullu operatör 5.16 [expr.cond] 'da açıklanmıştır. Paragraf 2, aşağıdaki metni içerir:

İkinci veya üçüncü işlenen (her ikisi de değil) bir atmak-ifadesi (15.1); sonuç diğerinin türündedir ve bir değerdir.

Bu, koşullu operatörden bir istisna atmasına izin verildiğini söylüyor. Bununla birlikte, diğer şube bir değer olsa bile, bu bir değer haline getirilir! Dolayısıyla koşullu bir ifadenin sonucuna bir değer katmak mümkün değildir. Durum, virgül operatörünü kullanarak yeniden yazmanın yanı sıra, koşullu operatörün sonucundan yalnızca değer elde etmek için yeniden yazılabilir:

template <typename It>
typename std::iterator_traits<It>::reference
access(It it, It end) {
    return *(it == end? throw std::runtime_error("no element"): it);
}

Biraz zor iş, bir const Fonksiyondan referans derler ama aslında bir referansı geçici olarak iade eder!


16
2017-12-30 22:00



Bu cevabı kabul etmeli, soruya cevap verdiğinden ve olası tuzaklar hakkında bilgi vermelisiniz. - David Rodríguez - dribeas


Standarttaki ifade 5.16 / 2 civarındadır:

Eğer ikinci veya üçüncü işlenende tip geçersizse, o zaman lvalue-to-rvalue (4.1), array-to-pointer (4.2) ve function-to-pointer (4.3) standart dönüşümleri ikinci ve üçüncü sırada gerçekleştirilir. işlenenler ve aşağıdakilerden biri tutulur:

- İkinci veya üçüncü işlenen (her ikisi de değil) bir atma ifadesidir (15.1); sonuç diğerinin türündedir ve bir değerdir.

Hangi aldığınız davranışları açıklar. Atmak yasaldır, ancak ifadenin türü bir saf rvalue (ifade bir lvalue) ve böylece bir const olmayan lvalue referans


13
2017-12-30 21:58





Bu şekilde yapılabilir:

return it == end? (throw std::runtime_error("no element"),*it): *it;

12
2017-12-30 21:55



Bu problemden kaçınmanın bir yolu, ama IMO *(it == end ? throw ... : it) Kıyafet görünüyor. - hvd
Heh, ve bu yorumu paylaştığım gibi Dietmar Kühl'ın kendi cevabını koyduğu tam olarak buydu. :) - hvd
@ hvd: Dışardaki dereferansı hareket ettirmek daha iyi görünüyorsa da, en azından kavramsal olarak tekrarlayıcıyı kopyalar. Virgül operatörünü kullanmak, yineleyiciyi kopyalama ihtiyacından kaçınmalıdır. - Dietmar Kühl
@ DietmarKühl Haklısınız. Bununla birlikte, kopyalanması pahalı olan bir yineleyici, endişelendiğim bir şey değildir ve yineleyici parametrelerini değere göre aldığınız gerçeği göz önüne alındığında, gerçekten de endişelenecek bir şey değil. Bu durumda, okunabilirlik benim için daha yüksek bir önceliğe sahiptir, ancak siz ve başkaları yasal olarak farklı sonuçlara varırsınız. - hvd
@hvd / Dietmar'ın çözümünüzün gerçek avantajı, ifadeyi tekrarlamak zorunda kalmamanızdır. Benim çözümümün avantajı, daha karmaşık ifadeler için bile mümkün olmasıdır (ve virgül operatörünün ardından ilk ifade kısaltılabilir). Belki de duruma göre hangi tarzın daha uygun olduğuna karar vermeliyiz. - Daniel Frey