Birincisi, "bu * için ref-elemeleri" sadece bir "pazarlama açıklamasıdır". *this
Asla değişmeyen tür, bu yazının altına bakın. Yine de bu ifadelerle anlaşılması çok daha kolay.
Ardından, aşağıdaki kod, † işlevinin "örtük nesne parametresi" nin ref niteleyicisine dayalı olarak çağrılacak işlevi seçer :
// t.cpp
#include <iostream>
struct test{
void f() &{ std::cout << "lvalue object\n"; }
void f() &&{ std::cout << "rvalue object\n"; }
};
int main(){
test t;
t.f(); // lvalue
test().f(); // rvalue
}
Çıktı:
$ clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic t.cpp
$ ./a.out
lvalue object
rvalue object
Her şey, işlevin çağrıldığı nesnenin bir değer olduğu (örneğin adsız geçici) olduğu gerçeğinden yararlanmanıza izin vermek için yapılır. Başka bir örnek olarak aşağıdaki kodu ele alalım:
struct test2{
std::unique_ptr<int[]> heavy_resource;
test2()
: heavy_resource(new int[500]) {}
operator std::unique_ptr<int[]>() const&{
// lvalue object, deep copy
std::unique_ptr<int[]> p(new int[500]);
for(int i=0; i < 500; ++i)
p[i] = heavy_resource[i];
return p;
}
operator std::unique_ptr<int[]>() &&{
// rvalue object
// we are garbage anyways, just move resource
return std::move(heavy_resource);
}
};
Bu biraz çelişkili olabilir, ama fikri almalısınız.
Eğer birleştirebilirsiniz o Not cv-eleme ( const
ve volatile
) ve ref-eleme ( &
ve &&
).
Not: Buradan sonra birçok standart tırnak ve aşırı yük çözünürlüğü açıklaması!
† Bunun nasıl çalıştığını ve @Nicol Bolas'ın cevabının en azından kısmen yanlış olduğunu anlamak için C ++ standardını biraz kazmamız gerekiyor (eğer @ Nicol'in cevabının neden yanlış olduğunu açıklayan kısım en altta, eğer sadece bununla ilgilenir).
Hangi fonksiyonun çağırılacağı aşırı yük çözünürlüğü adı verilen bir süreç tarafından belirlenir . Bu süreç oldukça karmaşıktır, bu yüzden sadece bizim için önemli olan noktaya dokunacağız.
İlk olarak, üye işlevleri için aşırı yük çözünürlüğünün nasıl çalıştığını görmek önemlidir:
§13.3.1 [over.match.funcs]
p2 Aday işlevleri kümesi, aynı bağımsız değişken listesine göre çözümlenecek hem üye hem de üye olmayan işlevleri içerebilir. Bu argüman ve parametre listelerinin bu heterojen kümede karşılaştırılabilmesi için, bir üye işlevinin, üye işlevinin çağrıldığı nesneyi temsil eden, örtük nesne parametresi olarak adlandırılan ekstra bir parametreye sahip olduğu kabul edilir . [...]
p3 Benzer şekilde, uygun olduğunda, bağlam, üzerinde çalışılacak nesneyi göstermek için zımni bir nesne bağımsız değişkeni içeren bir bağımsız değişken listesi oluşturabilir .
Neden üye ve üye olmayan işlevleri karşılaştırmamız gerekiyor? Operatör aşırı yükleme, bu yüzden. Bunu düşün:
struct foo{
foo& operator<<(void*); // implementation unimportant
};
foo& operator<<(foo&, char const*); // implementation unimportant
Aşağıdakilerin kesinlikle ücretsiz işlevi çağırmasını istersiniz, değil mi?
char const* s = "free foo!\n";
foo f;
f << s;
Bu nedenle üye ve üye olmayan işlevler aşırı yük setine dahil edilir. Çözünürlüğü daha az karmaşık hale getirmek için standart teklifin kalın kısmı vardır. Ayrıca, bu bizim için önemli olan nokta (aynı fıkra):
p4 Statik olmayan üye işlevleri için örtük nesne parametresinin türü
burada X
işlev üyesi olan ve sınıfıdır ev elemanı işlev bildiriminde cv-yeterlilik. [...]
p5 Aşırı yük çözünürlüğü sırasında [...] [t] nesne parametresini [...] örtük kılar, çünkü ilgili argümandaki dönüşümler şu ek kurallara uyacaktır:
[...]
(Son bit, bir üye işlevinin (veya operatörün) çağrıldığı nesnenin örtülü dönüşümlerine dayanarak aşırı yük çözünürlüğünü aldatamayacağınız anlamına gelir.)
Bu yazının üst kısmındaki ilk örneği ele alalım. Yukarıda bahsedilen dönüşümden sonra, aşırı yük seti şöyle görünür:
void f1(test&); // will only match lvalues, linked to 'void test::f() &'
void f2(test&&); // will only match rvalues, linked to 'void test::f() &&'
Daha sonra, örtük bir nesne argümanı içeren argüman listesi, aşırı yük kümesinde bulunan her fonksiyonun parametre listesiyle karşılaştırılır. Bizim durumumuzda, bağımsız değişken listesi yalnızca bu nesne bağımsız değişkenini içerecektir. Bunun nasıl göründüğüne bakalım:
// first call to 'f' in 'main'
test t;
f1(t); // 't' (lvalue) can match 'test&' (lvalue reference)
// kept in overload-set
f2(t); // 't' not an rvalue, can't match 'test&&' (rvalue reference)
// taken out of overload-set
Setteki tüm aşırı yükler test edildikten sonra, yalnızca bir tanesi kalırsa, aşırı yük çözünürlüğü başarılı olur ve dönüştürülen aşırı yüke bağlı işlev çağrılır. Aynı şey ikinci 'f' çağrısı için de geçerlidir:
// second call to 'f' in 'main'
f1(test()); // 'test()' not an lvalue, can't match 'test&' (lvalue reference)
// taken out of overload-set
f2(test()); // 'test()' (rvalue) can match 'test&&' (rvalue reference)
// kept in overload-set
Not Ancak, herhangi sağladığının ref-eleme yani (ve haliyle işlevi aşırı değil) f1
olur bir rvalue (hala maç §13.3.1
):
p5 [...] Ref niteleyicisi olmadan bildirilen statik olmayan üye işlevleri için ek bir kural geçerlidir:
- örtük nesne parametresi
const
-kalifiye edilmemiş olsa bile, diğer tüm açılardan argüman örtük nesne parametresinin türüne dönüştürülebildiği sürece parametreye bir rvalue bağlanabilir.
struct test{
void f() { std::cout << "lvalue or rvalue object\n"; }
};
int main(){
test t;
t.f(); // OK
test().f(); // OK too
}
Şimdi, @ Nicol'in cevabının neden en azından kısmen yanlış olduğuna. Diyor:
Bu bildirimin türünü değiştirdiğini unutmayın *this
.
Bu yanlış, *this
her zaman bir değerdir:
§5.3.1 [expr.unary.op] p1
Tekli *
operatör dolaylı işlem gerçekleştirir : uygulandığı ifade, bir nesne tipine bir işaretçi veya bir işlev tipine bir işaretçi olacaktır ve sonuç, ifadenin işaret ettiği nesneye veya işleve atıfta bulunan bir değerdir .
§9.3.2 [class.this] p1
Statik olmayan (9.3) üye işlevinin gövdesinde, anahtar sözcük this
değeri işlevin çağrıldığı nesnenin adresi olan bir ön değer ifadesidir. Tipi this
bir sınıfın bir üyesi işlevinde X
olduğu X*
. [...]