Giriş
Şu satırı dahil ederiz.#include <initializer_list>
Bu sınıf sadece bir diziye iterator tutar. Verilen diziyi kopyalamaz. Dolayısıyla oldukça ucuz bir sınıftır. Heap kullanmadığını görmek için şöyle yaparız
#include <string>
#include <iostream>
void* operator new(size_t size)
{
std::cout << "new overload called" << std::endl;
return malloc(size);
}
template <typename T>
void foo(std::initializer_list<T> args)
{
for (auto&& a : args)
std::cout << a << std::endl;
}
int main()
{
foo({2, 3, 2, 6, 7});
// std::string test_alloc = "some string longer than std::string SSO";
}
Açıklaması şöyle
The thing is, std::initializer_list does not hold the objects inside itself. When you instantiate it, compiler injects some additional code to create a temporary array on the stack and stores pointers to that array inside the initializer_list. For what its worth, an initializer_list is nothing but a struct with two pointers (or a pointer and a size):
Yani elimizde şöyle bir kod olsun
template <class T>
class initializer_list {
private:
T* begin_;
T* end_;
public:
size_t size() const { return end_ - begin_; }
T const* begin() const { return begin_; }
T const* end() const { return end_; }
// ...
};
Şöyle yapalım
foo({2, 3, 4, 5, 6});
Aslında kavramsal olarak şöyle bir kod üretilir
int __tmp_arr[5] {2, 3, 4, 5, 6};
foo(std::initializer_list{arr, arr + 5});
Kendimiz gerçekleştirsek
Bu sınıfı kendimiz gerçekleştirmek istersek
Örnek
Bu sınıfı kendimiz gerçekleştirmek istersek şöyle yaparız.
template<class _E>
class my_initializer_list
{
public:
typedef _E value_type;
typedef const _E& reference;
typedef const _E& const_reference;
typedef size_t size_type;
typedef const _E* iterator;
typedef const _E* const_iterator;
private:
iterator _M_array;
size_type _M_len;
// The compiler can call a private constructor.
constexpr my_initializer_list(const_iterator __a, size_type __l)
: _M_array(__a), _M_len(__l) { }
public:
constexpr my_initializer_list() noexcept
: _M_array(0), _M_len(0) { }
// Number of elements.
constexpr size_type
size() const noexcept { return _M_len; }
// First element.
constexpr const_iterator
begin() const noexcept { return _M_array; }
// One past the last element.
constexpr const_iterator
end() const noexcept { return begin() + size(); }
};
std::initializer_list ve constructorEffective Modern C++ kitabındaki açıklama şöyle. uniform initilization kullanılıyorsa std::initializer_list diğer parametreli constructor'a tercih edilir.
If, however, one or more constructors declare a parameter of type std::initializer_list, calls using the braced initialization syntax strongly prefer the overloads taking std::initializer_lists. Strongly. If there’s any way for compilers to construe a call using a braced initializer to be to a constructor taking a std::initializer_list, compilers will employ that interpretation.Örnek
Elimizde iki constructor sunan Foo sınıfı olsun
Foo(std::initializer_list<int>) {
std::cout << "with initializer list\n";
}
veFoo(int) {
std::cout << "with int\n";
}
Bu sınıfı şöyle çağıralımFoo a{10}; // new style initialization
Foo b(20); // old style initialization
Çıktı olarak şunu alırız.with initializer list
with int
auto Tanımlamaauto x={1,2,3};//auto deduces x to std::initializer_list
Şöyle yaparız.
auto items = {1,2,3}; //OK: items is inferred to be std::initializer_list<int>
//must #include <initializer_list>
Template Metodlar İle Kullanma
Örnek
Modern Effective C++'ta şöyle bir örnek ver. Elimizde şöyle bir metod olsun.template<class T>
void f(T t){...}
auto x={1,2,3};//auto deduces x to std::initializer_list
f(x);
Örnek
Şöyle yaparız.
Tek elemanlı listeler overload olan metodlarla iyi çalışmıyor. Açıklaması şöyle
std::initializer_list sonuç dönmek için kullanılamaz. Şöyle yapamayız.Şöyle yaparız.
template<typename T>
void f(T const & items);
f({1,2,3}); //ILL-FORMED: T cannot be deduced to be
//std::initializer_list<int> or anything else
//even if you use #include <initializer_list>
f(std::initializer_list<int>{1,2,3}); //OK: #include <initializer_list>
f(std::vector<int>{1,2,3});//OK: #include <vector>
//because std::vector accepts std::initializer_list<T>
Tek Elemanlı ListelerTek elemanlı listeler overload olan metodlarla iyi çalışmıyor. Açıklaması şöyle
Elimizde şöyle bir kod olsun. initializer_list vermemize rağmen int alan metod seçilir.
- Otherwise, if the parameter type is not a class and the initializer list has one element, the implicit conversion sequence is the one required to convert the element to the parameter type
void f (std::vector<int> v) {...}
void f (int n) {...}
f ({42}); // the int overload is being picked up
Çözümü çift parantez kullanmak. Şöyle yaparız.f ({{42}});
Eğer std::set alan bir f metodu daha olsaydı şöyle yaparız.f (std::vector<int>{42});
Return Valuestd::initializer_list<int> f() {
return {1,2,3};
}
auto f() -> std::initializer_list<int>
{
return {1,2,3};
}
Şu kod derlenmez.
std::initializer_list<int> x{1,2,3,4,5,6};
std::initializer_list<int const> y = x; // error! cannot convert!
Açıklaması şöyleIt sounds like your question is why std::initializer_list<T const> cannot be constructed from std::initializer_list<T> despite the fact that it would be easy to implement such a conversion.Initializer List for Döngüsü ile Dolaşılabilir
I think the answer is that you are not supposed to have std::initializer_list<T const> in the first place, given that, as you noted, std::initializer_list<T> only gives const access to its elements anyway.
It might therefore be said that there is a "cultural norm" in C++ that you are not supposed to have any constructors that require a std::initializer_list<T const> argument. None of the standard library containers do, for instance, since a cv-qualified value type is illegal anyway (except in the case of std::array, which, of course, has no user-defined constructors anyway).
std::initializer_list ve for Döngüsü yazısına taşıdım.
constructor - default
İmzası şöyle
İmzası şöyle.
İmzası şöyle
İmzası şöyle
constructor - default
İmzası şöyle
constexpr initializer_list() noexcept;
constructor - iterator + sizeİmzası şöyle.
// The compiler can call a private constructor.
constexpr initializer_list(const_iterator __a, size_type __l);
begin() metoduİmzası şöyle
// First element.
constexpr const_iterator begin() const noexcept;
Elimizde bir dizi olsun.
int elements[N];
std::initializer_list'i diziye kopyalamak için şöyle yaparızstd::initializer_list<int> data = ...;
size_t size = data.size();
if ( size <= N )
{
std::copy(data.begin(), data.end(), std::begin(elements));
}
else
{
std::copy(data.begin(), data.begin()+N, std::begin(elements));
}
end metoduİmzası şöyle
// One past the last element.
constexpr const_iterator end() const noexcept;
Şöyle yaparız.std::initializer_list<int> data = ...;
auto it = data.end();
size metodu
İmzası şöyle// Number of elements.
constexpr size_type size() const noexcept;
Şöyle yaparızstd::initializer_list<int> data = ...;
size_t size = data.size();
Hiç yorum yok:
Yorum Gönder