Giriş
{1,2,3} şeklinde tanımlanan "initializer list" ile std::initializer_list bağlantılı olsalar da aslında farklı şeyler. Bu yazıda std::initializer_list anlatılmıyor! Bu sınıf için std::initializer_list yazısına bakabilirsiniz.
İlklendirme
Açıklaması şöyle
"initializer list" bir nesneyi sınırsız sayıda aynı tipten parametre ile nesne {...} veya nesne = {...} şeklinde ilklendirmemizi sağlıyor. Derleyici initializer list ile karşılaşınca altta bizim için özel bir kod üretir. Bu yapının en büyük avantajı STL veriyapılarını kolaylıkla ilklendirebilmemizi sağlaması.
Şu kod ilklendirme sayılır mı emin değilim.
Order Of Evaluation
Açıklaması şöyle. Yani ilklendirme için verilen değerler, yazıldıkları sırada işlenir.
Initializer List'ten Önce Ne Yapıyorduk?
Şöyle constructor'lar yazıyorduk.
Initializer List Nesneleri Nasıl Saklar
Initializer list aslında sadece bir compiler trick'ten ibaret. Derleyici tarafından üretilen bir array'a işaret eder. Aşağıdaki kod parçası
Bu kısıma dikkat etmek gerekir. Örnek'te X nesnesinin initializer_list için kopyasının çıkarıldığı görülebilir. Dolayısıyl x,y,z için copy constructor 3 defa çağrılır. Variadic templates kopya çıkarmadan direkt reference ile çalışabilir.
Elimizde şöyle bir sınıf olsun. Copy constructor'ın 3 defa çağrıldığını görebiliriz.
Initializer List'in Kopyalanması
Initializer list nesnesini copy-construct ile başka bir yerde kullanmaya çalışmak tehlikeli olabilir. Initializer list yerine içindekiler vector gibi bir container sınıfa kopyalanmalıdır.
Kendi sınıflarımız için şöyle yaparız
Sınıfın default constructor metodu ve Initializer List alan constructor metodu varsa ve initializer list boş ise, default constructor tercih edilir. C++11 ile STL sınıflarının default constructor metodları explicit oldukları için bazı durumlarda derleme hatası alabiliriz. Örnek'te unordered_map kullanımı hata verir.
STL Sınıflarının Initializer List ile Kullanımı
Bu kullanım şekli bir çok programlama dilinde var. Örneğin C#'taki ismi Collection Initializers. Bizi bir çok Add çağrısından kurtarıyor.
Array
Örnek:
{1,2,3} şeklinde tanımlanan "initializer list" ile std::initializer_list bağlantılı olsalar da aslında farklı şeyler. Bu yazıda std::initializer_list anlatılmıyor! Bu sınıf için std::initializer_list yazısına bakabilirsiniz.
İlklendirme
Açıklaması şöyle
[C++14: 5.17/9]: A braced-init-list may appear on the right-hand side of
- an assignment to a scalar, in which case the initializer list shall have at most a single element. The meaning of x={v}, where T is the scalar type of the expression x, is that of x=T{v}. The meaning of x={} is x=T{}.
- an assignment to an object of class type, in which case the initializer list is passed as the argument to the assignment operator function selected by overload resolution (13.5.3, 13.3).
Şu kod ilklendirme sayılır mı emin değilim.
#include <iostream>
#include <vector>
int f() { std::cout << "f"; return 0;}
int g() { std::cout << "g"; return 0;}
void h(std::vector<int> v) {}
int main() {
h({f(), g()});
}
Order Of Evaluation
Açıklaması şöyle. Yani ilklendirme için verilen değerler, yazıldıkları sırada işlenir.
§8.5.4.4: Within the initializer-list of a braced-init-list, the initializer-clauses, including any that result from pack expansions (§14.5.3), are evaluated in the order in which they appear.
Initializer List'ten Önce Ne Yapıyorduk?
Şöyle constructor'lar yazıyorduk.
widget( T[] init, int length ) // (1)
widget( T... init ) // (2)
widget( std::vector<T> init ) // (3)
Initializer List Nesneleri Nasıl Saklar
Initializer list aslında sadece bir compiler trick'ten ibaret. Derleyici tarafından üretilen bir array'a işaret eder. Aşağıdaki kod parçası
std::vector<double> v {1, 2, 3.14};
aslında derleyici tarafından şu hale getirilir. Derleyici tarafından const bir array yaratıldığına dikkat ediniz.const double temp[] = {double(1), double(2), 3.14 } ;
initializer_list<double> tmp(temp,
sizeof(temp)/sizeof(double));
vector<double> v(tmp);
Initializer List lvalue'ların Kopyasını AlırBu kısıma dikkat etmek gerekir. Örnek'te X nesnesinin initializer_list için kopyasının çıkarıldığı görülebilir. Dolayısıyl x,y,z için copy constructor 3 defa çağrılır. Variadic templates kopya çıkarmadan direkt reference ile çalışabilir.
#include <utility>
#include <iostream>
struct X
{
X() { }
X(X const &x) { std::cout << "X(const&)" << std::endl; }
X(X&&) { std::cout << "X(X&&)" << std::endl; }
};
void foo(std::initializer_list<X> const& l) { }
int main()
{
X x, y, z, w;
foo({x, y, z, std::move(w)}); // Will print "X(X const&)" three times
// and "X(X&&)" once
}
ÖrnekElimizde şöyle bir sınıf olsun. Copy constructor'ın 3 defa çağrıldığını görebiliriz.
struct Foo {
Foo() {
std::cout << "default ctor" << std::endl;
}
Foo(const Foo&) {
std::cout << "copy ctor" << std::endl;
}
Foo(std::initializer_list<Foo>) {
std::cout << "initializer_list<Foo>" << std::endl;
}
};
Şöyle yaparız.int main()
{
Foo a; // default ctor
Foo b{a, a, a}; // copy ctor + copy ctor + copy ctor + initializer_list<Foo>
return 0;
}
Initializer List'in Kopyalanması
Initializer list nesnesini copy-construct ile başka bir yerde kullanmaya çalışmak tehlikeli olabilir. Initializer list yerine içindekiler vector gibi bir container sınıfa kopyalanmalıdır.
Şu açıklama da benzer şeyleri söylüyor.The underlying array is not guaranteed to exist after the lifetime of the original initializer list object has ended. The storage for std::initializer_list is unspecified (i.e. it could be automatic, temporary, or static read-only memory, depending on the situation).
Tehlike mantık olarak şu koddan kaynaklanıyor.An object of type initializer_list<E> provides access to an array of objects of type const E. [...] Copying an initializer list does not copy the underlying elements. [...]
int * f(int a)
{
int* p = &a;
return p; //bug waiting to happen
}
initializer_list<int> g(int a, int b, int c)
{
initializer_list<int> v = { a, b, c };
return v; // bug waiting to happen
}
Initializer List Alan ConstructorKendi sınıflarımız için şöyle yaparız
MyClass(std::initializer_list<typename std::map<T, int>::value_type> l)
:
_objects(l)
{
}
Bu sınıfı construct etmek içinMyClass<char*> myObject =
{
{ "One", 600 }, // Each of these entires will cause the creation of
{ "Two", 200 }, // a temporary object of type:
{ "Three", 50 }, // std::pair<char* const, int>
{ "Four", 10 }, // that will become an element of the initializer
{ "Five", 1 }, // list received by the constructor.
};
Default Constructor ve Initializer ListSınıfın default constructor metodu ve Initializer List alan constructor metodu varsa ve initializer list boş ise, default constructor tercih edilir. C++11 ile STL sınıflarının default constructor metodları explicit oldukları için bazı durumlarda derleme hatası alabiliriz. Örnek'te unordered_map kullanımı hata verir.
#include <unordered_map>
void foo(const std::unordered_map<int,int> &) {}
int main()
{
foo({});
}
STL Sınıflarının Initializer List ile Kullanımı
Bu kullanım şekli bir çok programlama dilinde var. Örneğin C#'taki ismi Collection Initializers. Bizi bir çok Add çağrısından kurtarıyor.
List<string> l = new List<string>() { ".JPG", ".JPE", ".BMP", ".GIF", ".PNG" };
Aynı mantık C++ ile de geçerli.Array
Örnek:
std::array<int, 2> a{1,2};
Vector
Hiç yorum yok:
Yorum Gönder