21 Ağustos 2017 Pazartesi

Initializer List

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
[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).
"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.
#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ır
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.
#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
}
Örnek
Elimizde şö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.
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).
Şu açıklama da benzer şeyleri söylü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. [...]
Tehlike mantık olarak şu koddan kaynaklanıyor.
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 Constructor
Kendi 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çin
MyClass<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 List
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.
#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
Örnek:
std::vector<std::vector<double>> data { {1,2,3}, {1,2,3}, {1,2,3} };
Shared_ptr
Örnek:
std::shared_ptr<int> ptr(new int[5]{1,2,3,4}, std::default_delete<int[]>());







Hiç yorum yok:

Yorum Gönder