C++ Dilinde Reference Collapsing
- Yusuf Hançar
- May 5, 2024
- 3 min read
Updated: May 20, 2024
Başta C++ programlama dili olmak üzere birçok modern programlama dilinde, pointerlara ve referanslara sık sık başvurulur. Pointerlar, bellekteki bir değişkenin adresini işaret eden değerlerdir, referanslar ise bir değişkenin başka bir ismiyle çağrılmasını sağlar. Ancak, referanslarda pointerların aksine, referansların referansı olan bir yapı doğrudan desteklenmez.
Ancak, bazı durumlarda, özellikle belirli bir bağlamda, referansların referansı gibi davranılması gerekebilir. Bu tür durumlar, "reference collapsing" olarak bilinen bir kural seti tarafından ele alınır. Bu kurallar, bu tür durumları belirli bir türe indirgeyerek çözümlemeyi sağlar.
Bu yazımızda, pointerların pointerına işaret edebilme yeteneğine sahipken referansların böyle bir yeteneğinin olmamasının nedenlerini ve reference collapsing(C++11) mekanizmasının nasıl çalıştığını inceleyeceğiz. Ayrıca, bu kavramların C++ ve diğer dillerde nasıl uygulandığını ve neden bu tür özelliklerin programlama dünyasında önemli olduğunu tartışacağız.
T& - & | T& |
T& - && | T& |
T&& - & | T& |
T&& - && | T&& |
Reference collapsing hangi senaryolarda karşımıza çıkar ?
a) template parametrelerinde...
Burada T&&, "universal reference" olarak adlandırılan bir yapıdır. T&&, T'nin bir referans olup olmadığına veya sağ taraf referansı (rvalue reference) olduğuna bağlı olarak farklı şekillerde davranabilir.
Bu durumda, T&& hem lvalue referanslarını (T&) hem de sağ taraf referanslarını (T&&) kapsar. Yani, smart_func fonksiyonuna argüman olarak hem bir lvalue hem de bir rvalue geçilebilir.
Bu örnekte, T'nin türüne bağlı olarak reference collapsing meydana gelir. Örneğin, T bir int& olduğunda, T&& bir int& && olur ve bu iki referans birleştirilerek int&'e dönüşür. Benzer şekilde, T bir int olduğunda, T&& doğrudan int&& olur. Reference collapsing, bu gibi durumlarda template kullanımının güvenli ve esnek olmasını sağlar.
template <typename T>
void smart_func(T&& arg);
b) tür eş isim bildirimlerinde yani using kullanımında...
class SmartCode{};
using left = SmartCode&;
using right = SmartCode&&;
int main()
{
SmartCode sc;
left& val = SmartCode{};
}
*****************************
AÇIKLAMA : reference collapsing ile sol taraf referansına bağlndı. R value expression ile bağlamak hata.
*****************************
CEVAP : syntax error
*****************************
int main()
{
SmartCode sc;
left& val = sc;
}
*****************************
CEVAP : legal
*****************************
int main()
{
SmartCode sc;
left&& val = sc;
}
*****************************
CEVAP : legal
*****************************
int main()
{
SmartCode sc;
right& val = sc;
}
*****************************
CEVAP : legal
*****************************
int main()
{
SmartCode sc;
right&& val = sc;
}
*****************************
AÇIKLAMA : sağ taraf referansına sol taraf değeri bağlanamaz.
*****************************
CEVAP : syntax error
*****************************
c) tür çıkarımında...
int main()
{
int val{3};
int* ptr = &val;
decltype(*ptr)& rval = val;
}
*****************************
AÇIKLAMA : reference collapsing vardır.
decltype(*ptr) burdan L value reference elde edilir yani burada int& vardır.
int& to & olunca int& e bağlandı ve collapse oldu.
*****************************
template konusu ve universal reference gibi bağlamları bilmemiz gerekmektedir. İlerleyen yazılarda bunlara detaylıca değineceğiz!!!
int val = 5;
int& r1 = val; // Lvalue reference
int&& r1 = std::move(val); // Rvalue reference
int&& r3 = 10; // Rvalue reference
// Reference collapsing ...
template<typename T>
void smart_func(T&& arg) {}
int main()
{
smart_func(r1); // Lvalue reference olarak kalır
smart_func(r2); // Rvalue reference olarak kalır
smart_func(24); // Lvalue reference olarak kalır
return 0;
}
Genellikle template kullanılarak çok amaçlı fonksiyonlar ve sınıflar yazılır. Bu şekilde yazılan template fonksiyonlar, hem "Lvalue" hem de "Rvalue" argümanları kabul edebilmek için "universal references" olarak adlandırılan referanslar kullanır. Ancak, bu referansların davranışı bazen beklenmedik sonuçlar doğurabilir ve C++ dilinde "std::forward" gibi bir araç ve reference collapsing ilişkilidir.
C++ dilinde, referanslarla çalışırken, referansların kendileri birbirlerine atanamadıklarını söylemiştik. Ancak, template kullanıldığında, referansların referanslara dönüşebildiği durumlar ortaya çıkabilir. Özellikle, bir fonksiyonun template olarak tanımlanması ve bu fonksiyona bir "Lvalue" argümanın geçirilmesi durumunda, referans collapsing meydana gelir. Bu durumda, derleyici, "Lvalue" referansını bir "Rvalue" referansına dönüştürür.
std::forward<T> içindeki T, genellikle bir template parametresi olarak tanımlanır ve bu parametre, "Rvalue" veya "Lvalue" referansının bilgisini içerir. std::forward, bu bilgiyi kullanarak, referansın orijinal türünü korur ve uygun dönüşümü gerçekleştirir.
Referans collapsing, iki referansın birbirine atanmasının mümkün olup olmadığını ve sonuç referansının ne olacağını belirler. std::forward, referansların dönüşümünü yönetirken, referans collapsing etkilerini gözetir.
C++ dilinde, referanslar ve template'lerin karmaşık davranışları bazen anlaşılması zor olabilir. Ancak, std::forward gibi araçlar ve referans collapsing gibi kavramlar, geliştiricilere daha güvenli ve esnek kod yazma imkanı sunarak programların performansını artırabilir.

Comments