C ++ 17 Güncellemesi
C ++ 17'de, A_factory_func()
geçici bir nesne (C ++ <= 14) oluşturmanın anlamı, bu ifadenin C ++ 17'de başlatıldığı (gevşekçe) herhangi bir nesnenin başlatılmasını belirlemeye kadar değişti. Bu nesneler ("sonuç nesneleri" olarak adlandırılır), bir bildirim tarafından oluşturulan değişkenlerdir ( a1
başlatma gibi ), başlatma işlemi sona erdiğinde oluşturulan yapay nesneler veya referans ciltleme için bir nesne gerekiyorsa (örneğin, içinde A_factory_func();
. Son durumda, "geçici materyalizasyon" adı verilen bir nesne yapay olarak oluşturulur, çünkü A_factory_func()
aksi takdirde bir nesnenin var olmasını gerektiren bir değişken veya referansa sahip değildir).
Bizim durumumuzda örnek olarak, özel kurallar a1
ve a2
özel kurallar söz konusu olduğunda, bu tür bildirimlerde, a1
değişkenle aynı türdeki bir başlangıç başlatıcısının sonuç nesnesinin değişken olduğunu a1
ve dolayısıyla A_factory_func()
nesneyi doğrudan başlattığını söyler a1
. Herhangi bir işlevsel işlevsel tarzdaki dökümün herhangi bir etkisi olmayacaktır, çünkü A_factory_func(another-prvalue)
sadece dış ön değerin sonuç nesnesi, aynı zamanda iç ön değerin sonuç nesnesi olarak "geçer".
A a1 = A_factory_func();
A a2(A_factory_func());
Hangi türün A_factory_func()
döndüğüne bağlıdır. A
Kopya oluşturucu açık olduğunda, o zaman birincisi başarısız olur dışında - o zaman aynı yapıyor - varsayalım . Oku 8.6 / 14
double b1 = 0.5;
double b2(0.5);
Bu aynı şeyi yapıyor çünkü yerleşik bir tip (burada bir sınıf tipi değil). 8.6 / 14'ü okuyun .
A c1;
A c2 = A();
A c3(A());
Bu aynı şeyi yapmıyor. İlk varsayılan A
, POD olmayan bir değer olup olmadığını başlatır ve POD için herhangi bir başlatma yapmaz (Okuma 8.6 / 9 ). İkinci kopya başlatılır: Değer bir geçici değeri başlatır ve sonra bu değeri c2
(Read 5.2.3 / 2 ve 8.6 / 14 ) içine kopyalar . Bu tabii ki açık olmayan bir kopya oluşturucu gerektirecektir (Read 8.6 / 14 and 12.3.1 / 3 and 13.3.1.3/1 ). Üçüncüsü, bir işlevi c3
döndüren A
ve A
( geri 8.2 ) döndüren bir işleve bir işlev işaretçisi alan bir işlev için işlev bildirimi oluşturur .
Başlatmalara Doğrudan Ayrılma ve Kopyalama başlatma
Aynı görünseler ve aynı şeyi yapmaları gerekiyorsa da, bu iki form bazı durumlarda oldukça farklıdır. İki başlatma şekli doğrudan ve kopya başlatmadır:
T t(x);
T t = x;
Her birine atfedebileceğimiz davranışlar var:
- Doğrudan başlatma, aşırı yüklenmiş bir işleve bir işlev çağrısı gibi davranır: Bu durumda işlevler,
T
( explicit
olanlar dahil ) yapıcılarıdır ve argüman x
. Aşırı yük çözünürlüğü, en iyi eşleşen yapıcıyı bulur ve gerektiğinde örtük dönüştürme gerekir.
- Kopya başlatma işlemi, örtük bir dönüştürme sırası oluşturur:
x
Tür nesnesine dönüştürmeye çalışır T
. (Daha sonra bu nesnenin üzerinden başlatılan nesneye kopyalanabilir, bu nedenle bir kopya oluşturucu da gereklidir - ancak bu aşağıda önemli değildir)
Gördüğünüz gibi, kopya başlatma , bir şekilde olası örtük dönüşümlerle ilgili doğrudan başlatmanın bir parçasıdır: Doğrudan başlatma, tüm kurucuları arayabilir ve buna ek olarak , argüman türlerini eşleştirmek için gereken örtük dönüşümü yapabilir, kopya başlatma yalnızca bir örtülü dönüşüm dizisi oluşturabilir.
Ben sert çalıştı ve yapıcılar aracılığıyla "bariz" kullanmadan, bu formların her biri için farklı metin çıkışı için aşağıdaki kodu aldımexplicit
.
#include <iostream>
struct B;
struct A {
operator B();
};
struct B {
B() { }
B(A const&) { std::cout << "<direct> "; }
};
A::operator B() { std::cout << "<copy> "; return B(); }
int main() {
A a;
B b1(a); // 1)
B b2 = a; // 2)
}
// output: <direct> <copy>
Nasıl çalışır ve bu sonucu neden çıkarır?
Doğrudan başlatma
İlk önce dönüşüm hakkında hiçbir şey bilmiyor. Sadece bir kurucu çağırmaya çalışacaktır. Bu durumda, aşağıdaki kurucu kullanılabilir ve tam eşleşir :
B(A const&)
Bu kurucuyu çağırmak için gerekli olan, daha az kullanıcı tanımlı bir dönüşüm yoktur (burada herhangi bir nitelik yeterlilik dönüşümü de gerçekleşmez). Ve böylece doğrudan başlatma bunu çağırır.
Kopya başlatma
Gibi, başlatma bir dönüştürme sekansı inşa edecek kopyalama yukarıda adı geçen a
tip değil B
(burada açık bir şekilde olduğu) ya da bundan türetilen. Bu yüzden, dönüşüm yapmanın yollarını arayacak ve aşağıdaki adayları bulacak
B(A const&)
operator B(A&);
Dönüştürme işlevini nasıl yeniden yazdığıma dikkat edin: Parametre türü this
, sabit olmayan üye işlevinde sabit olmayan işaretçi türünü yansıtır . Şimdi bu adayları x
argüman olarak adlandırıyoruz. Kazanan dönüşüm fonksiyonudur: Çünkü aynı tipe referansı kabul eden iki aday fonksiyonumuz varsa, o zaman daha az const versiyonu kazanır (bu arada, const olmayan üye fonksiyonunu tercih eden mekanizma da -sık nesneler).
Dönüştürme işlevini sabit üye işlevi olarak değiştirirsek, dönüştürme belirsizdir (çünkü her ikisinde de bir parametre türü vardır A const&
): Comeau derleyicisi bunu düzgün bir şekilde reddeder, ancak GCC bunu bilgiçlik dışı modda kabul eder. Buna geçmek -pedantic
, bunun da uygun belirsizlik uyarısı vermesini sağlar.
Umarım bu, bu iki formun nasıl farklılaştığını daha net hale getirmeye yardımcı olur!
A c1; A c2 = c1; A c3(c1);
.