17 Ağustos 2020 Pazartesi

Brace Initialization - Aggregate

Giriş
Aggreate POD veya array olabilir. Array için olan yazıyı ayırmaya karar verdim. Brace Initialization - Array Aggregate yazısına taşıdım.

1. Aggregate Nedir
C++11 ile tanım olarak şöyle.
An aggregate is an array or a class with no user-declared constructors, no private or protected non-static data members, no base classes, and no virtual functions.
C++17 ile tanım olarak şöyle. Yani artık public inheritance olabiliyor.
no virtual, private, or protected (since C++17) base classes
Örnek - C++14 Default Member Initializer'a İzin Verir
C++11 açısından default member initializer kullanıldığı için şu bir aggregate değil. C++14 açısından aggregate sayılır
class S
{
  public:
    int a = 0;
};
Aggregate ve Initializer List
Açıklaması şöyle.
When you initialize it using an initializer list, the elements in the list will initialize the first n members of the aggregate, where n is the number of elements in the list. The remaining elements of the aggregate are copy-list-initialized.
Örnek - Explicit Constructor Olamaz
Elimizde şöyle bir kod olsun. a sınıfındaki constructor explicit olduğu için hata alırız.
class a
{
public:
    explicit a () {}
};
struct b
{
    a member;
};

int main() {
    b foo {};
}
Bu kod derlenmez. Hata çıktısı şöyledir.
"chosen constructor is explicit in copy-initialization"
Aggregate ve Kalıtım
Kalıtım sadece C++17 ile geldiği için açıklaması şöyle
Each direct public base, (since C++17) array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.
Örnek - C++17 Kalıtıma İzin Verir
Dolayısıyla C++17 açısından B bir aggregate kabul edilir.
struct A { int a; };

struct B : A {};
Örnek -  - C++17 Kalıtıma İzin Verir 
Elimizde şöyle bir kod olsun. İlk kod kalıtım olduğu halde aggregate initialization kullanıyor.
struct Base0 {};
struct Base1 {};

template<typename... Ts>
struct Derived: Ts... {};

int main() {
  Derived<Base0, Base1> d0 {Base0{}, Base1{}}; // OK
  Derived<Base0, Base1> d1 (Base0{}, Base1{}); // ERROR
}
User Provided Constructor Yoksa ve User Declared Constructor Varsa
C++20 ile geriye uyumluluk bozuluyor. C++17'ye kadar derlenen kodlar artık derlenmiyor.
Örnek
Açıklaması şöyle
Class Ax is an aggregate in C++11, C++14 and C++17, as it has no user-provided constructors, which means that Ax{} is aggregate initialization, bypassing any user-declared constructors, even deleted ones.
..
In C++20 the rules were changed so that any user-declared constructors prevent the type from being an aggregate, and so the example will fail to compile.
Şu kod C++17'ye kadar derlenir. C++20 ile derlenmez.
class Ax
{    
public:
    
  Ax() = delete;
  Ax(Ax const&)=delete;
  Ax(Ax&&)=delete;
  void operator=(Ax const&)=delete;
  void operator=(Ax&&)=delete;

  void print()
  {
    cout << "Hello \n";
  }
};

int main(int argc, char** argv) 
{           
  Ax{}.print();
   return 0;
}
Örnek
Açıklaması şöyle. C++20 ile artık aggregate initializaton çalışmıyor.
C++ currently allows some types with user-declared constructors to be initialized via aggregate initialization, bypassing those constructors. The result is code that is surprising, confusing, and buggy. This paper proposes a fix that makes initialization semantics in C++ safer, more uniform,and easier to teach. We also discuss the breaking changes that this fix introduces.
Örnek
Şu kod derlenmez.
struct X {
  int i{4};
  X() = default;
};

int main() {
  X x1(3); // ill-formed - no matching c’tor
  X x2{3}; // compiles!
}
Örnek
Şu kod derlenmez.
struct Foo
{
    Foo() = default;
    int bar;
};
auto test = Foo { 0 };
Default Constructor'ı Silmek
Elimizde şöyle bir kod olsun.
struct Foo {
  Foo() = delete;
  int i;
};
Şöyle yaparız böylece üye alanlara bir değer atanır.
Foo f2{};   // i initialized to zero
Foo f3{42}; // i initialized to 42
Şu kod derlenmez.
Foo f1;     // i uninitialized
Public Olmayan Constructor - C++17
Elimizde şöyle bir kod olsun
struct B {
protected:
    B() { }
};

struct D : B { };

auto d = D{};
Açıklaması şöyle
In C++14, D is not an aggregate because it has a base class, so D{} is "normal" (non-aggregate) initialization which invokes D's default constructor, which in turn invokes B's default constructor. This is fine, because D has access to B's default constructor.

In C++17, the definition of aggregate was widened - base classes are now allowed (as long as they're non-virtual). D is now an aggregate, which means that D{} is aggregate initialization. And in aggregate-initialization, this means that we (the caller) are initializing all the subobjects - including the base class subobject. But we do not have access to B's constructor (it is protected), so we cannot invoke it, so it is ill-formed.
2. Brace Initialization Örnekleri

Member sayısından Daha Az Değer Belirtmek - C++ 98
Bu yöntem C dilinde de aynı. Açıklaması şöyle
The {0} initializer to initialize all objects in an aggregate (meaning array or struct) is 100% standard in any version of C. The syntax allows you to omit the braces for sub-aggregates.
Örnek
Şöyle yaparız. Buradaki örnek C kodu. Hem struct hem de array olan değişken ilklendiriliyor.
struct my_type my_thing[NUMBER_OF_THINGS] = {0};
Örnek
Şöyle yaparız.
SHELLEXECUTEINFO sexi = {0};
Boş aggregate initializer da aynı şeyi yapar.
SHELLEXECUTEINFO sexi = {};
char mytext[100] = {};
Burada dikkat edilmesi gereken nokta alignment yüzünden yerleştirilen aradaki byte'lar aggregate ile sıfırlanmaz. Bu iş için memset yapmak gerekir. Yani
struct foo
{
  char c;
  int  i;
};

foo a = {0};
ile
foo a;
memset(&a,0,sizeof(a));
farklı şeylerdir.

Member Sayısından Daha Az Değer Belirtmek - C++11
Açıklaması şöyle.
If there are fewer initializers in the list than there are members in the aggregate, then each member not explicitly initialized shall be default-initialized. Example:
struct S { int a; char* b; int c; };
S ss = { 1, "asdf" };
initializes ss.a with 1ss.b with "asdf", and ss.c with the value of an expression of the form int(), that is, 0.
Member Sayısından Daha Az Değer Belirtmek - C++14
C++14 ile şöyle yapabiliyoruz. Elimizde şöyle bir struct olsun
struct PointA
{
  double x = 0.0;
  double y = 0.0;
};
Şöyle yapabiliriz.
PointA pA3 = { 2.0 }; // {2., 0.}
Member Sayısı Kadar Değer Belirtmek
Tüm elemanlar belirtilen değerlere sahip olur. 

Örnek
Elimizde şöyle bir kod olsun
template <typename T, size_t dim1, size_t dim2>
struct MyStruct
{
public:
    T m_data[dim1][dim2];
};
Şöyle yaparız
MyStruct<int, 3, 3> s = 
{{
    { 1, 2, 3 },
    { 4, 5, 6 },
    { 7, 8, 9 },
}};
Sequence Point
Açıklaması şöyle.
10) In list-initialization, every value computation and side effect of a given initializer clause is sequenced before every value computation and side effect associated with any initializer clause that follows it in the brace-enclosed comma-separated list of initalizers.
Liste içinde aynı değişken değer değiştirerek tekrar kullanılabilir. Şöyle yapabiliriz.
struct T t = { i, ++i };




Hiç yorum yok:

Yorum Gönder