Bir dönüşüm operatörünün bu aşırı yüklenmesi neden seçildi?


27

Aşağıdaki kodu düşünün .

struct any
{
    template <typename T>
    operator T &&() const;

    template <typename T>
    operator T &() const;
};
int main()
{
    int a = any{};
}

Burada ikinci dönüştürme operatörü aşırı yük çözünürlüğü ile seçilir. Neden?

Anladığım kadarıyla, iki operatör sırasıyla operator int &&() constve hesabından çıkarılır operator int &() const. Her ikisi de uygulanabilir fonksiyonlar kümesinde. [Over.match.best] ile okumak, ikincisinin neden daha iyi olduğunu anlamama yardımcı olmadı.

İkinci işlev neden öncekinden daha iyidir?


Ben basit bir kediyim ve ikinci kez olması gerektiğini söyleyebilirim, diğerinin tanıtımı C ++ 11'de kırıcı bir değişiklik olurdu. Standartlarda bir yerlerde olacak. İyi soru olsa da, bir oy verin. (Dikkat uzmanları: Bu yorum hogwash ise lütfen bana söyleyin!)
Bathsheba

3
FWIW, template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;birincisini aramasını sağlar.
NathanOliver

5
@LightnessRaceswithMonica Dönüştürme işleçleri izin verirse, aksi takdirde birden çok dönüştürme işleçine sahip olmanın hiçbir yolu olmaz.
NathanOliver

1
@Bathsheba Bunun nedeni olduğunu sanmıyorum. Bu, hareket oluşturucuların hiçbir zaman aşırı yük çözünürlüğü ile seçilemeyeceğini söylemek gibidir, çünkü bu bir kırılma değişikliği olacaktır. Bir hamle yapıcısı yazarsanız, kodunuzun bu tanım tarafından "kırılmış" olmasını tercih edersiniz.
Brian

1
@LightnessRaceswithMonica Kafamda düz tutmamın yolu, dönüş tipine işlevin adı olarak davranmaktır. Farklı türler farklı isimlere eşittir başarı :)
NathanOliver

Yanıtlar:


13

Geri dönen dönüşüm işleci, geri dönen dönüşüm işlecinden T&daha özel olduğu için tercih edilir T&&.

Bkz. C ++ 17 [temp.deduct.partial] / (3.2):

Bir dönüştürme işlevine yapılan çağrı bağlamında, dönüştürme işlevi şablonlarının dönüş türleri kullanılır.

ve / 9:

Belirli bir tür için tümdengelim her iki yönde de başarılı olursa (yani, türler yukarıdaki dönüşümlerden sonra özdeş ise) ve her ikisi de Pve Areferans türleriyse (yukarıda belirtilen türle değiştirilmeden önce): - bağımsız değişken şablonundan tür ise bir değer değeri başvurusuydu ve parametre şablonundan tür değildi, parametre türünün en azından bağımsız değişken türü kadar uzman olduğu düşünülmedi; ...


12

Çıkarılan getiri değeri dönüştürme operatörleri biraz gariptir. Ancak ana fikir, hangisinin kullanıldığını seçmek için bir işlev parametresi gibi davranmasıdır.

Ve arasındaki karar verirken T&&ve aşırı yük çözünürlüğü kuralları kazanır. Buna izin vermek için:T&T&

template<class T>
void f( T&& ) { std::cout << "rvalue"; }
template<class T>
void f( T& ) { std::cout << "lvalue"; }

çalışmak. T&&bir lvalue ile eşleşebilir, ancak hem lvalue hem de evrensel referans aşırı yükleri mevcut olduğunda lvalue bir tercih edilir.

Doğru dönüşüm operatörleri kümesi muhtemelen:

template <typename T>
operator T&&() &&;

template <typename T>
operator T &() const; // maybe &

ya da

template <typename T>
operator T() &&;

template <typename T>
operator T &() const; // maybe &

başarısız ömür boyu uzantısının sizi ısırmasını önlemek için.

3 Sıralamayı belirlemek için kullanılan türler, kısmi sıralamanın yapıldığı bağlama bağlıdır:

[SNIR]

(3.2) Bir dönüştürme işlevine yapılan çağrı bağlamında, dönüştürme işlevi şablonlarının dönüş türleri kullanılır.

Daha sonra aşırı yükleri seçerken "daha özel" kurallara bağlı olarak sona erer:

(9.1) bağımsız değişken şablonundan tür bir değer değeri başvurusu ise ve parametre şablonundan tür değilse, parametre türünün en azından bağımsız değişken türü kadar uzman olduğu düşünülmez; aksi takdirde,

Dolayısıyla operator T&&, en azından bu kadar uzman değil operator T&, bu arada hiçbir kural devleti operator T&en az onun kadar uzmanlaşmış değildir operator T&&, bu yüzden operator T&daha uzmanlaşmıştır operator T&&.

Daha özel şablonlar, aşırı yük çözünürlüğünü daha azdan kazanır, diğer her şey eşittir.


Birisi yanıtlarken dil-avukat etiketini ekledi ; Sadece silebilirdim. Ben metin okuma belli belirsiz bir hafızaya sahip denir hangisinin almak için bir parametre ve metin olarak ele alınacağına dikkat devletler o bahsediyor nasıl &&vs &şablon aşırı yükleri toplama, ancak tam metni ipuçları vermeyi bir süre alacağını zaman tedavi edilir .
Yakk - Adam Nevraumont

4

Biz başlatmak için çalışıyoruz intbir mesafede any. Bunun için süreç:

  1. Bunu nasıl yapabileceğimizi anlayın. Yani, tüm adaylarımızı belirleyin. Bunlar, intstandart bir dönüşüm dizisi ( [over.match.conv] ) aracılığıyla dönüştürülebilen müstehcen olmayan dönüşüm işlevlerinden gelir . Bölüm şu cümleyi içerir:

    “Referans X” ifadesi döndüren bir dönüştürme işlevine yapılan çağrı , tür bir çokluktur Xve bu nedenle, bu tür bir dönüştürme işlevinin, Xaday işlevlerin seçilmesi işlemine yol açtığı düşünülür .

  2. En iyi adayı seçin.

1. adımdan sonra iki adayımız var. operator int&() constve operator int&&() consther ikisinin de intaday işlevlerin seçilmesi amacıyla verim sağladığı düşünülmektedir . En iyi aday hangisi int?

Rvalue referanslarına ( [over.ics.rank] /3.2.3 ) lvalue referanslarını tercih eden bir tiebreaker var . Burada gerçekten bir referans bağlamıyoruz ve örnek biraz ters çevrilmiş - bu, parametrenin lvalue ve rvalue referansı olduğu durum içindir.

Bu geçerli değilse , daha uzmanlaşmış işlev şablonunu tercih etme [over.match.best] /2.5 tiebreaker'a düşeriz .

Genel olarak konuşursak, başparmak kuralı daha spesifik dönüşüm en iyi eşleşmedir. Değer referansı dönüştürme işlevi, yönlendirme referansı dönüştürme işlevinden daha belirgindir, bu nedenle tercih edilir. intBaşlattığımız şey hakkında bir rvalue gerektiren hiçbir şey yoktur (bunun yerine bir başlangıç int&&yapmış operator T&() constolsaydık , o zaman aday olmazdı).


3.2.3 aslında T&&kazanır diyor değil mi? Ah, ama bağlı bir değerimiz yok. Yoksa biz mi? Adaylarımızı seçerken, bazı kelimeler nasıl edindiğimizi tanımlamalı int&ve int&&bu bir bağlayıcı mıydı?
Yakk - Adam Nevraumont

@ Yakk-AdamNevraumont Hayır, rvalue referanslarına lvalue referanslarını tercih eder. Bence bu muhtemelen yanlış bölüm ve "daha özel" tiebreaker doğru olanı.
Barry
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.