top of page

C++ Dilinde aggregate

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • Nov 10, 2024
  • 19 min read

C++ dilinde aggregate (bileşik) türler, veri elemanlarını doğrudan erişime açan ve herhangi bir gizleme veya koruma mekanizması (data hiding) olmayan sınıflardır. Aggregate sınıflarda, sınıfın tüm veri elemanları doğrudan sınıfın arabiriminin bir parçası olarak kabul edilir ve sınıfın iç mantığını (invariant) koruma gibi bir yükümlülüğü yoktur. Bu nedenle, veri elemanları public access seviyesinde olur ve programcılar bu veri elemanlarına doğrudan erişebilir.


Neden Aggregate Sınıf Oluşturulur?

Bir sınıfta gizlenecek veya korumaya ihtiyaç duyulan bir bilgi yoksa, bu sınıfı aggregate olarak tanımlamak uygun olabilir. Örneğin, sadece veriyi tutan ve herhangi bir iş mantığı veya sınıf içi kısıtlama barındırmayan basit veri yapıları aggregate olarak tanımlanabilir. Aggregate türlerin sağladığı avantajlardan biri de nesnelerin listelerde veya dizilerde kolayca başlatılabilmesidir.


Aggregate Tür Olma Koşulları

C++ dilinde bir sınıfın aggregate olarak kabul edilmesi için belirli kurallar vardır. Bu kurallar, C++17 ve C++20 gibi standartlarda bazı değişiklikler içerse de, genel olarak aşağıdaki gibidir:


  • User Declared Kurucu İşlevlerinin Olmaması:

    • Aggregate sınıfların user declared (kullanıcı tarafından bildirilmiş) kurucu işlevleri olmamalıdır. Ancak burada dikkat edilmesi gereken bir ayrım vardır:

  • User Declared ile User Defined farklıdır. Bir kurucu işlev = default veya = delete şeklinde bildirilmişse, bu durumda C++17'de aggregate olarak kabul edilirdi.

  • C++20'de ise bu kural daha katı hale getirilmiştir; artık herhangi bir user declared kurucu işlevi olan sınıf aggregate olarak kabul edilmez.


struct MyStruct {
    MyStruct() = default;   // User declared fakat user defined değil; C++17'de aggregate, C++20'de değil.
    MyStruct() = delete;    // Aynı durum, C++17'de aggregate, C++20'de değil.
};

  • Non-Static Veri Elemanlarının Public Olması:

    • Aggregate bir sınıfta tüm non-static veri elemanlarının public erişim seviyesinde olması gerekir. Veri gizleme yapılmayan bu sınıflar, tüm veri elemanlarını dışarıya açarak erişim kolaylığı sağlar.


  • Diğer Kısıtlar:

    • Kalıtım kullanmaması: Bir aggregate sınıf, herhangi bir başka sınıftan türememelidir.

    • Private veya protected base sınıfı bulunmamalıdır.

    • Private veya protected virtual base sınıfı olmamalıdır.


Bu kurallar çerçevesinde, bir sınıf yukarıdaki özellikleri taşıyorsa aggregate olarak kabul edilir ve aggregate initialization gibi özel başlatma yöntemleri kullanılabilir.



Diziler ve Aggregate Türler


  • Diziler, aggregate türler olarak kabul edilirler. Bir dizinin elemanlarının aggregate olup olmaması önemli değildir; dizi türleri her durumda aggregate olarak değerlendirilir. Örneğin, std::string sınıfı bir aggregate tür değildir, ancak std::string türünden bir dizi (std::string str[5]) aggregate olarak kabul edilir.


Aggregate Türlerin Belirlenmesi


  • C++17 öncesi standartlarda bir türün aggregate olup olmadığını anlamanın doğrudan bir yolu yoktu. C++17 ile type_traits başlık dosyasına std::is_aggregate adlı bir meta fonksiyon eklenmiştir. std::is_aggregate sınıfının değeri, türün bir aggregate olup olmadığına göre true veya false olacaktır. Ek olarak is_aggregate_v adlı variable template de kullanıma sunulmuştur.


#include <type_traits>
#include <iostream>

struct Agg { int x, y; };
struct NonAgg { private: int x; };

int main() 
{
   std::cout << std::boolalpha;

   std::cout << "Agg aggregate? " << std::is_aggregate<Agg>::value << "\n";      // true
   std::cout << "NonAgg aggregate? " << std::is_aggregate<NonAgg>::value << "\n"; // false
}

Aggregate Initialization
  • Aggregate sınıflar, doğrudan başlatma listeleri ({}) kullanılarak kolayca başlatılabilir. Bu, sınıfın veri elemanlarını sırasıyla başlatmak için kullanılır.

struct Point {
    int x;
    int y;
};

Point p = {10, 20}; // Aggregate init

struct Data {
    int x, y;
};

int main()
{
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");
}    
*****************************     
CEVAP : aggregate
*****************************

struct Data {
    int x, y;

private :
    int z;
};

int main()
{
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");
}    
*****************************     
CEVAP : static assertion failed: not an aggregate
*****************************

int main()
{
    static_assert(std::is_aggregate_v<std::string>, "not an aggregate");
}    
*****************************     
CEVAP : static assertion failed: not an aggregate
*****************************

Normalde string aggreagte değil ancak aggregate olma koşulunda sınıfın tüm elemanları aggregate olması koşulu yoktur.
struct Data {
    int x, y;
    std::string str;
};

int main()
{
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");
}  
*****************************     
CEVAP : aggregate
*****************************

C++17 öncesi kalıtımla elde edilen sınıflar aggregate type olamaz.
  • c++17 ile public inheritance ile elde edilen sınıflar aggregate type olabilir.


class Base {};

class Derive : public Base {};

int main()
{
    static_assert(std::is_aggregate_v<Derive>, "not an aggregate");
} 
*****************************     
AÇIKLAMA : public kalıtım şartı var.
*****************************     
CEVAP : aggregate
*****************************

class Derive : public std::string {
public :
    int x, y;
};

int main()
{
    static_assert(std::is_aggregate_v<Derive>, "not an aggregate");
}    
*****************************     
AÇIKLAMA : aggregate olmayan bir taban sınıftan kalıtım yoluyla elde edilen sınıf diğer koşulları sağlarsa aggregate type olur.
*****************************     
CEVAP : aggregate
*****************************

class Data {
private :
    int mx{};
};

class Derive : public std::string {
public :
    int x, y;
    Data val;
};

int main()
{
    static_assert(std::is_aggregate_v<Derive>, "not an aggregate");
}    
*****************************     
CEVAP : aggregate
*****************************

aggregate type olabilmesi için C++17 ile gelen virtual inheritance olmamalıdır.
class Data {
private :
    int mx{};
};

class Derive : virtual Data {
public :
    int x, y;
};

int main()
{
    static_assert(std::is_aggregate_v<Derive>, "not an aggregate");
}    
*****************************     
CEVAP : not an aggregate
*****************************

multiple inheritance aggregate type durumunu engeller.
class A{};
class B{};
class C{};

class Derive : public A, B, C {
public :
    int x, y;
};

int main()
{
    static_assert(std::is_aggregate_v<Derive>, "not an aggregate");
}    
*****************************     
CEVAP : not an aggregate
*****************************

class A {
public :
    A (int);
    A (double);
};

class Derive : public A {
public :
    int x, y;
};

int main()
{
    static_assert(std::is_aggregate_v<Derive>, "not an aggregate");
}    
*****************************     
CEVAP : aggregate
*****************************

inherited constructor durumunda aggregate type olmaz.
class A {
public :
    A (int);
    A (double);
};

class Derive : public A {
public :
    using A::A;
    int x, y;
};

int main()
{
    static_assert(std::is_aggregate_v<Derive>, "not an aggregate");
}    
*****************************     
CEVAP : not an aggregate
*****************************

Aggregate türlerin reference elemanları olabilir.
class Data {
public :
    int x, y;
    int& r1;
    int&& r2;
};

int main()
{
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");
}    
*****************************     
CEVAP : aggregate
*****************************

Aggregate türlerin member fonksiyonları olabilir. Ve bunların private ya da protected access specifier durumları önemli değildir.
class Data {
public :
    int x, y;

private :
    int pri_func();

protected :
    int pro_func();
};

int main()
{
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");
}    
*****************************     
CEVAP : aggregate
*****************************

Aggregate türlerin virtual fonksiyonlara sahip olamazlar.
class Data {
public :
    int x, y;

    virtual void foo(){}
};

int main()
{
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");
}     
*****************************     
CEVAP : not an aggregate
*****************************

C++14 ile default member initializer eklendi ve aggregate type olmasına engel değildir.

Feature-test macro

Value

Std

Feature

__cpp_nsdmi

200809L

c++11

__cpp_aggregate_nsdmi

201304L

c++14


class Derive {
public :
    int x = 1;
    int y = 2;
};

int main()
{
    static_assert(std::is_aggregate_v<Derive>, "not an aggregate");
}    
*****************************     
CEVAP : not an aggregate
*****************************

lambda ifadelerinden elde edilen türler aggregate type değildir.
int main()
{
    auto fn = [](){};

    using us_fn = decltype(fn);

    static_assert(std::is_aggregate_v<us_fn>, "not an aggregate");
}  
*****************************     
AÇIKLAMA : c++17
*****************************     
CEVAP : not an aggregate
*****************************

int main()
{
    static_assert(std::is_aggregate_v<decltype([](){})>, "not an aggregate");  
}  
*****************************     
AÇIKLAMA : c++20 ile gelen unevaluated context ile bu şekilde yazabiliriz.
*****************************     
CEVAP : not an aggregate
*****************************

aggregate type olmayan türlerden kalıtım yoluyla aggregate type oluşturulabildiği için lambda ifadelerinde kalıtım yoluyla elde edilen sınıflar aggregate type olabilir.

auto f1 = [](int x){ return x * x; };
auto f2 = [](int x){ return x * 5; };

class Data : public decltype(f1), public decltype(f2) {
public :
    int val;
};

int main()
{
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");  
}  
*****************************     
CEVAP : aggregate
*****************************

bir aggregate türün elemanının dizi olması aggregate olmasına engel değildir.
class Data {
public :
    std::string arr[10];
};

int main()
{
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");  
}  
*****************************     
CEVAP : aggregate
*****************************

aggregate türlerin operator fonksiyonlarının olması aggregate olmasına engel değildir.
class Data {
public :
    int a, b;

    Data operator+(const Data&) const;
};

int main()
{
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");  
}  
*****************************     
CEVAP : aggregate
*****************************

Aggregate olmasının faydası nedir ?
  • aggregate initialization sentaks ile aggregate türlerden nesne oluşturulabilir.


class Data {
public :
    int a, b, c;
};

int main()
{
    Data d = { 1, 5, 9 };  
}
*****************************     
AÇIKLAMA : aggregate initialization
*****************************     
CEVAP : legal
*****************************

class Data {
public :
    int a, b, c;
};

int main()
{
    Data d{ 1, 5, 9 };  
}  
*****************************     
AÇIKLAMA : aggregate initialization açısından bir fark yok
*****************************     
CEVAP : legal
*****************************

class Data {
public :
    int a, b, c;
};

int main()
{
    Data d = { 1, 5, 9.5 };  
}  
*****************************     
CEVAP : error: narrowing conversion of ‘9.5e+0’ from ‘double’ to ‘int’ [-Wnarrowing]
*****************************

class Data {
public :
    int a;
    double dval;
    std::string str;
    int arr[3];
};

int main()
{
    Data d = { 10, 5.4, "cpp", 2, 7, 6 };  
}  
*****************************     
CEVAP : legal
*****************************

class First {
public :
    int a, b;
};

class Data {
public :
    int a;
    First f;
    int arr[3];
};

int main()
{
    Data d = { 10, { 5, 9 }, { 2, 5, 8 } };  
}  
*****************************     
AÇIKLAMA : Data d = { 10, 5, 9, 2, 5, 8 };  bu da legal
*****************************     
CEVAP : legal
*****************************

class Data {
public :
    int a, b;
    bool c;
    std::string str;
};

int main()
{
    Data d{ 20 };
} 
*****************************     
AÇIKLAMA : geriye kalan value initialize edilir ve sıfır olur.
*****************************     
CEVAP : legal
*****************************

int main()
{
    int arr1[3] = { 2, 5, 8 };
    int arr2[3]{ 2, 5, 8 };    // modern c++ ile geçerli kılındı. C dilinde de yok.
    int arr3[]{ 2, 5, 8 };     // legal
}
*****************************     
AÇIKLAMA : C dilinde de legal C++ dilinde aggregate initialization olarak geçer.
***************************** 

int main()
{
    std::string arr[4] = { "cpp", "20" };

    std::cout << arr[2].size();
}  
*****************************     
AÇIKLAMA : value init olduğu için sıfır. Aynı kural aggregate türler içinde geçerlidir.
*****************************     
CEVAP : 0
*****************************

class Data {
public :
    int a, b;
    bool c;
    std::string str;
};

int main()
{
    Data d = { 17, 20 };

    std::boolalpha(std::cout);

    std::cout << "d.a : " << d.a << "\n";
    std::cout << "d.c : " << d.c << "\n";
    std::cout << "d.str.size() : " << d.str.size() << "\n";

    static_assert(std::is_aggregate_v<Data>, "not an aggregate");  
} 
*****************************     
CEVAP : 
d.a : 17
d.c : false
d.str.size() : 0
*****************************

aggregate türlerin static veri elemanları olabilir
class Data {
public :
    int a, b;
    static int val;
    inline static std::string s{ "cpp" };
    std::string str;
};

int main()
{
    Data d = { 17, 20 };

    std::boolalpha(std::cout);

    std::cout << "d.b : " << d.b << "\n";
    std::cout << "d.str.size() : " << d.str.size() << "\n";
    
    static_assert(std::is_aggregate_v<Data>, "not an aggregate");  
}
*****************************     
AÇIKLAMA : aggregate
*****************************     
CEVAP : 
d.b : 20
d.str.size() : 0
*****************************

aggregate türden sınıfa doğrudan eşittir ile ilk değer verme sentaksı yoktur.
class Data {
public :
    int a;
};

int main()
{
    Data d = 5;    
}  
*****************************     
AÇIKLAMA : legal olabilmesi için ya d = { 5 } şeklinde ya da d{ 5 } şeklinde init edilmelidir.
*****************************     
CEVAP : error: conversion from ‘int’ to non-scalar type ‘Data’ requested
*****************************

int main()
{
    std::array<int, 3> arr = { 1, 2 };
    std::array<int, 3> arr1{ 1, 2 };    // diğer öğeler value init edilir.
} 
*****************************     
AÇIKLAMA : array sınıfının vector sınıfındaki gibi initializer_list parametreli ctor'ı yoktur doğrudan aggregate türdür. legal olmasının nedeni aggregate initialization.
*****************************     
CEVAP : legal
*****************************

template <typename T, std::size_t n>
struct Array {
    T arr[n];
};

int main()
{
    Array<int, 3> arr = { 1, 2 }; // legal

    static_assert(std::is_aggregate_v<Array>, "not an aggregate");
}  
*****************************     
AÇIKLAMA : error: type/value mismatch at argument 1 in template parameter list for ‘template constexpr const bool std::is_aggregate_v<_Tp>’
*****************************     
CEVAP : not an aggregate
*****************************

indetermined value tanımsız davranışa yol açar.
struct Data {
    int x, y;
};

int main()
{
    Data d;  // aggregate 

    std::cout << d.x;
}  
*****************************     
AÇIKLAMA : indetermined value yüzünden geçerli değildir
*****************************     
CEVAP : undefined behaviour
*****************************

int main()
{
    std::array<int, 3> arr;

    for (auto val : arr)
    {
        std::cout << val;
    }
}  
*****************************     
AÇIKLAMA : indetermined value yüzünden geçerli değildir(garbage value)
yani aggregate olmasıyla ilgilidir.
*****************************     
CEVAP : undefined behaviour
*****************************

int main()
{
    std::array<int, 3> arr{};

    for (auto val : arr)
    {
        std::cout << val;
    }
}  
*****************************     
CEVAP : 000
*****************************

C++17 ile gelen structured binding aggregate türlerde kullanılabilir.

struct Data {
    int x, y;
    std::string str;
};

int main()
{
    Data d{ 10, 30, "cpp" };

    auto [a, b, s] = d;
}  
*****************************     
AÇIKLAMA :  aggregate olması nedeniyle geçerlidir.
aggregate olmasaydı get interface'ini destekleyecek öğeleri oluşturmamız gerekirdi.
*****************************     
CEVAP : legal
*****************************

default member initialize geçerlidr.
struct Data {
    int k;
    int x = 10;
    std::string str{ "cpp" };
    double d;
};

int main()
{
    Data d = { 10 };

    std::cout << d.x << "\n";
    std::cout << d.str << "\n";
    std::cout << d.d << "\n";
}    
*****************************     
CEVAP : 
10
cpp
0
*****************************

C++20 ile
  • Artık aggregate olabilmesi için user declared constructor olması gerekir

  • C++20 öncesi user provided constructor olmaması gerekirdi.


  • Derleyicilerin extension olarak sunduğu ve C dilinde 99 standartlarıyla ile dile eklenen designated initializer syntax array için kullanılabilir.

  • C++ dilindeki ise tüm aggregate türler için değil, array olmayan sınıf olan aggregate türler için kullanılır.


designated initializer syntax
int main()
{
    int arr[10] = { [2] = 4, [8] = 10 };
}  
*****************************     
AÇIKLAMA :  kalan öğeler sıfır olur.
ayrıca sıralı olmak zorunda da değildir.
-> int arr[10] = { [9] = 4, [3] = 10 }; bu da geçerli
*****************************     
CEVAP : C dilinde geçerli C++ dilinde diziler için geçerli değil
*****************************

int main()
{
    int arr[] = { [8] = 4, [4] = 10 };
}  
*****************************     
AÇIKLAMA : dizinin boyutunu belirtmezsek en büyük index 8 olduğundan dizinin boyutunu derleyici 9 olarak kabul eder.
*****************************     
CEVAP : C dilinde geçerli C++ dilinde diziler için geçerli değil
*****************************

int main()
{
    int arr[] = { [8] = 4, [4] = 10, 25 };
}  
*****************************     
CEVAP : C dilinde geçerli C++ dilinde diziler için geçerli değil
*****************************

struct Data {
    int a, b, c;
    char str[10];
    int arr[5];
};

int main()
{
    struct Data mydata {
        .a = 5
    };
} 

struct Data {
    int a, b, c;
    char str[10];
    int arr[5];
};

int main()
{
    struct Data mydata {
        .arr = { 5, 8, 10 }
    };
}  
*****************************     
AÇIKLAMA : diğer öğeler gene zero init edilir.
*****************************  

struct Data {
    int a, b, c;
    char str[10];
    int arr[5];
};

int main() {
    struct Data mydata = {
        .b = 10,               
        .arr = { [2] = 6 }   
    };

    printf("b: %d\n", mydata.b);
    printf("arr[2]: %d\n", mydata.arr[2]);

    return 0;
}
*****************************     
CEVAP : C dilinde geçerli C++ dilinde diziler için geçerli değil
*****************************

C++20 standartlarında designated initializer syntax aggregate türler için olmalıdır! " https://www.cppstories.com/2021/designated-init-cpp20/ "
class Data {
public :
    int a, b;
};

int main()
{
    Data d = { .a = 10, .b = 5 };
}  
*****************************     
AÇIKLAMA : eşittir olup olmaması designated initializer sentaksını etkilemez.
    Data d{ .a = 10, .b = 5 };
*****************************    

class Data {
public :
    int a, b;
};

int main()
{
    Data d = { .a = 10 };
}  
*****************************     
AÇIKLAMA : bütün öğelere ilk değer vermek zorunda değiliz. kalanlar value init edilir.
*****************************   

class Data {
public :
    int a, b;
};

int main()
{
    Data d = { .b = 10, .a = 5 };
}  
*****************************     
AÇIKLAMA : sırasını değiştiremeyiz!!!!!!
*****************************     
CEVAP : syntax error
*****************************

C++20 standartlarında designated initializer ile normal initializer birlikte kullanılamaz.
class Data {
public :
    int a, b;
    double dval;
};

int main()
{
    Data d = { .a = 10, .b = 5, 5.8 };
}  
*****************************     
AÇIKLAMA : C++20 error message : 
error: either all initializer clauses should be designated or none of them should be
*****************************     
CEVAP : syntax error
*****************************

Sınıfın static veri elemanlarını initialize etmek söz konusu değildir.
class Data {
public :
    int a, b;
    double dval;
    static int si;
};

int main()
{
    Data d = { .si = 10 };
}  
*****************************     
AÇIKLAMA : error: ‘Data’ has no non-static data member named ‘si’
*****************************     
CEVAP : syntax error
*****************************

struct Time {
    int min;
    int hour;
};

struct Date {
    int year;
    int month;
    int day;
    Time time;
    static int hmode;
};

int main()
{
    // invalid -static member
    Date d1 = { .hmode = 0 };

    // invalid -wrong order
    Date d2 = { .month = 3, .year = 2023 };
    
    // invalid -mix init
    Date d3 = { 3, .year = 2022 };

    // invalid - nested member access is not allowed
    Date d4 = { .time.min = 24 };

    // valid
    Date d5 = { .time = { 32, 4 } };
} 

struct Time {
    int min;
    int hour;
};

struct Date {
    int year;
    int month;
    int day;
    Time time;
    static int hmode;
};

int main()
{
    Date d = { .time = { .min = 23 } };
} 

struct Time {
    int min;
    int hour;
};

struct Date {
    int year;
    int month;
    int day;
    Time time;
    static int hmode;
};

int main()
{
    Date d = 
    { 
        .year{ 2023 },
        .day{ 29 },
        .time{ .hour{ 20 } }
    };
} 

default member initializer ile kullanılabilir.
struct Data {
    int a;
    int b = 5;
    std::string str{ "cpp" };
    double d;
};

int main()
{
    Data d= { .d = 5.8 };
}  
*****************************     
AÇIKLAMA : default member init var ama aggregate type olduğu için designated init kullanılabilir.
*****************************     
CEVAP : legal
*****************************

Kullanım senaryoları 1 :
  • Kodun okunurluğunu kolaylaştırır

  • Kodlama hatası riskini azaltır


struct Point {
    int y;
    int z;
    int x;
};

int main()
{
    Point p{ 1, 5, 9 };
}  
*****************************     
AÇIKLAMA : burda x 1 olarak atanmış sanılabilir. ancak designated init sentaksı bu duruma karşı önlemdir.
***************************** 

struct Point {
    int y;
    int z;
    int x;
};

int main()
{
    Point p{ .x = 1, .y = 5, .z = 9 };
}  
*****************************     
AÇIKLAMA : sırayı değiştirdik
*****************************     
CEVAP : error: designator order for field ‘Point::y’ does not match declaration order in ‘Point’
*****************************

Kullanım senaryoları 2 :
  • Fonksiyonların geri dönüş türünün aggragate olması durumdur.

  • Özellikle mandatory copy elision durumundan faydalanılan durumlarda.


struct Sofware {
    int id;
    std::string lang{ "cpp" };
    int standart;
};

Sofware get_feature()
{
    // code
    
    return Sofware{ .id = 11, .lang{ "cpp" }, .standart{ 23 } };
}

int main()
{
    auto per = get_feature();
    auto [id, lang, standart] = get_feature();
}  
*****************************     
AÇIKLAMA : fonksiyonun return türünün derleyici Sofware olarak gördüğü için tür ismi yazmadan şu şekilde de yazabiliriz.
    return { .id = 11, .lang{ "cpp" }, .standart{ 23 } };
*****************************   


Kullanım senaryoları 3:
  • Fonksiyonların parametre değişken sayısının fazla olması kodun yazılması ve okunmasını zorlaştırır ve hata yapma riskini artırır.

  • Ardışık parametreler aynı türden ya da benzer türlerden ise yani doğrudan ya da dolaylı olarak örtülü dönüşümle aynı argumanları alabilecek türlerdense yanlışlıkla bir parametreye geçilecek değeri bir başka parametreye geçebiliriz.


void func (double alpha, double beta, double teta){}

  • Designated initializer kullanımı parametrelerin bir arada paketlenip aggregate türün (structure'ın) elemanları yapıp parametreyi de aggregate type yapma şansımız olur.


void process_file (bool open, bool close, bool read, bool write){}

int main()
{
    process_file(false, true, true, false);
}  

struct FileProp {
    bool open;
    bool close; 
    bool read;
    bool write;
};

void process_file (const FileProp& fp){}

int main()
{
    process_file(FileProp{ false, true, true, false });
    process_file({ false, true, true, false });
}  

struct FileProp {
    bool open;
    bool close; 
    bool read;
    bool write;
};

void process_file (const FileProp& fp){}

int main()
{
    process_file(
    { 
        .open = false, 
        .close = true, 
        .read = true, 
        .write = false 
    });
}  
*****************************     
AÇIKLAMA : designated initializer kullanımı ile geçici nesne oluşturduk 
***************************** 

Designated initializer function overloading için de kullanılabilir.
struct Point {
    int x, y;
};

struct Point3D {
    double dx, dy;
};

void process(const Point&)
{
    std::cout << "const Point& " << "\n";
}

void process(const Point3D&)
{
    std::cout << "const Point3D& " << "\n";
}

int main()
{
    process({ .x = 15 });
}  
*****************************     
AÇIKLAMA : tür çıkarımını yapacak ve hangi overload çağırılacak anlayacak.
*****************************     
CEVAP : const Point&
*****************************

C++17 standartındaki diğer problem deduction guide ile ilgili :
  • template ve aggregate type bir sınıf olsun :


template <typename T>
struct Data {
    T val;
    int ival;
};

int main()
{
    Data<double> d{ 2.5, 58 };
    Data<std::string> s{ "cpp", 11 };
}  
*****************************     
AÇIKLAMA : T türünün aggreagate type olma zorunluluğu olmadığına göre Data sınıfının herhangi bir specialization'ı aggregate type olacaktır.
***************************** 

template <typename T>
struct Data {
    T val;
    int ival;
};

int main()
{
    Data d{ 2.5, 58 };      // c++17 error
    Data s{ "cpp", 26 };    // c++17 error
}  
*****************************     
AÇIKLAMA : iş CTAD faydasını kullanmaya gelince C++17 standartında error
çözüm ise deduction guide (c++20 legal)
*****************************  

template <typename T>
struct Data {
    T val;
    int ival;
};

template <typename T>
Data(T, int)->Data<T>;

int main()
{
    Data d{ 2.5, 58 };      // legal
    Data s{ "cpp", 23 };    // legal
}  
*****************************     
AÇIKLAMA : iş CTAD faydasını kullanmaya gelince C++17 standartında error
çözüm ise deduction guide ile alttaki template yazılmak zorundaydı.
*****************************     
CEVAP : c++20 olsa yazmaya gerek yoktu.
*****************************

template <typename T>
struct Data {
    T val;
    int ival;
};

int main()
{
    Data d{ "cpp", 17 };      // c++20 legal    
}  
*****************************     
AÇIKLAMA : d türü Data<string> açılımı değil Data<const char*> açılımıdır.
*****************************     
CEVAP : c++17 error c++20 legal
*****************************

template <typename T>
struct Data {
    T val;
    int ival;
};

Data(const char*, int)-> Data<std::string>;

int main()
{
    Data d{ "cpp", 20 };         
}  
*****************************     
AÇIKLAMA : burda deduction guide'a ihtiyaç yoktur ancak deduction yönü değiştirmek için deduction guide yazabiliriz.
*****************************     
CEVAP : c++17 and 20 legal
*****************************

template <typename T>
struct A {
    T x, y;
};

template <typename T>
A(T, T)-> A<T>;

int main()
{
    A ax = { 5, 8 };
}  
*****************************     
AÇIKLAMA : c++17
c++20 de deduction guide için alltaki template yazılmasa da legal
*****************************     
CEVAP : c++17 and 20 legal
***************************

container da aggregate tutulduğunda  bu şekilde insert işlemi yapıldığında okunurluk artar.

struct Software {
    int id;
    std::string lang{ "cpp" };
    int standart;
};

int main()
{
    std::vector<Software> pvec;

    pvec.push_back(Software{ .id = 11 });
    pvec.push_back(Software{ .lang = "developer" });
    pvec.push_back(Software{ .standart = 17 });
    pvec.push_back(Software{ .id = 14, .lang = "c++", .standart = 20 });

    for (const auto& [id, lang, standart] : pvec)
    {
        std::cout << id << " " << lang << " " << standart << "\n";
    }
}  
*****************************     
AÇIKLAMA : 
*****************************     
CEVAP : 
11 cpp 0
0 developer 0
0 cpp 17
14 c++ 20
*****************************

struct Data {
    int x, y, z;
};

int main()
{
    auto p = new Data(10, 20, 30); 

    delete p; 
}  
*****************************     
AÇIKLAMA : direct initilaization error sebebi
*****************************     
CEVAP : c++17 syntax error, c++20 legal
*****************************

struct Data {
    int x, y, z;
};

int main()
{
    auto up = std::make_unique<Data>(10, 20, 30); 
}  
*****************************  
AÇIKLAMA : direct initilaization error sebebi
*****************************     
CEVAP : c++17 syntax error, c++20 legal
*****************************

struct Data {
    int x, y, z;
};

int main()
{
    std::vector<Data> dvec;

    dvec.emplace_back(1, 5, 9);
}  
*****************************     
AÇIKLAMA : direct initilaization error sebebi
emplace_back aldığı argumanları perfect forward eder conrainer'ın sağladığı bellek alanında nesne oluşturabilmek için.
*****************************     
CEVAP : c++17 syntax error, c++20 legal
*****************************

template <typename T, typename ...Args>
std::unique_ptr<T> MakeUnique(Args&& ...args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

int main()
{
    auto up = MakeUnique<std::string>(10, 'A');

    std::cout << *up;
}  
*****************************     
CEVAP : AAAAAAAAAA     
*****************************

aggregate türlerin c++20 standartlarına kadar direct initialization'ı yoktu.
template <typename T, typename ...Args>
std::unique_ptr<T> MakeUnique(Args&& ...args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

// aggregate type
struct Data {
    int a, b, c;
};

int main()
{
    auto up = MakeUnique<Data>(10, 'A');
}  
*****************************     
AÇIKLAMA : c++17
aggregate türler make_unique, emplace fonksiyonlarında, new ifadelerinde kullanılamıyordu.
problem ise  std::forward<Args>(args)...) burdan kaynaklanır.
aggregate türlerin 2020 standartlarına kadar direct initialization'ı yoktu.
C++20 ile bazı kısıtlamalarla direct initialization ile oluşturulabilmesi sentaksı geldi.
*****************************     
CEVAP : c++17 syntax error, c++20 legal
*****************************

int main()
{
    int x = 5;
    int y = 4;
    int z = 3; 

    std::cout << (x, y) << "\n";
}    
*****************************     
AÇIKLAMA : x, y  bir ifadedir. virgül operatoruyla oluşturulan ifadenin türü sağ operandın değeridir.
*****************************     
CEVAP : 4
*****************************

int main()
{
    int x = 5;
    int y = 4;
    int z = 3;

    int val = x, y, z;   

    std::cout << val << "\n";
}    
*****************************     
AÇIKLAMA :
*****************************     
CEVAP :
error: redeclaration of ‘int y’
error: redeclaration of ‘int z’
*****************************

int main()
{
    int x = 5;
    int y = 4;
    int z = 3;

    int val = (x, y, z);   

    std::cout << val << "\n";
}    
*****************************     
AÇIKLAMA : burada val değişkenine z ile ilk değer verildi.
*****************************     
CEVAP : 3
*****************************

struct Data {
    int x, y;
    double dval;
};

int main()
{
    auto p = new Data(10, 20, 5.4);

    delete p;
}    
*****************************     
AÇIKLAMA :  c++17 error
error: new initializer expression list treated as compound expression
*****************************     
CEVAP : c++20 legal
*****************************

int main()
{
    double dval = 5.4;

    int sc{ dval };
}    
*****************************     
AÇIKLAMA : c++20 ve c++17
*****************************     
CEVAP : narrowing conversion warning
*****************************

int main()
{
    double dval = 5.4;

    int sc(dval);
}    
*****************************     
AÇIKLAMA : c++20 ve c++17
*****************************     
CEVAP : legal
*****************************

Aggregate türlerde, parantez kullanarak yapılan ilk değer atamalarda narrowing (daraltıcı) dönüşümler söz dizim hatasına yol açmaz. Bu durum, özellikle generic (genel amaçlı) kodlarda, derleyicinin kontrol kapsamına bağlı olarak gözden kaçabilir.
struct Data {
    int x, y;
    double dval;
};

int main()
{
    Data d(10, 2.5, 5.4);
}    
*****************************     
AÇIKLAMA : bu durumda y değişkeni de double türden init etmiş oluruz. 
{} parantez kullansaydık syntax error olurdu. Ancak derleyiciler yine kontrol ettikleri için uyarı mesajı verecektir.
generic kodlarda derleyinin kontrolünden kaçma ihtimali vardır.
*****************************     
CEVAP : c++17 error
c++20 legal
*****************************

aggregate türleri veri elemanlarını initialize ederken emplace_back fonksiyonuna perfect forward amaçlı gönderebiliriz.
struct Data {
    int x, y;
    double dval;
};

int main()
{    
    std::vector<Data> dvec;

    dvec.emplace_back(10, 20, 5.4);
}    
*****************************     
AÇIKLAMA : error: new initializer expression list treated as compound expression
*****************************     
CEVAP : c++17 error
c++20 legal
*****************************

aggregate türler için; direct initialization ile aggregate initialization arasında her ikisi de geçerli olmasına rağmen anlam farkı olan yerler vardır.
  • life-extension

  • aggregate türlerin elemanların reference type olabilir.

int foo()
{
    return 1;
}
    
int main()
{    
    int& r = foo();
}    
*****************************     
AÇIKLAMA : cannot bind non-const lvalue reference of type ‘int&’ to an rvalue of type ‘int’
*****************************     
CEVAP : syntax error çünkü ilk değer veren ifade PR value expression
*****************************

int foo()
{
    return 1;
}
    
int main()
{    
    const int& r = foo();
}    
*****************************     
AÇIKLAMA : life extension
const L value ref PR value ifadeye bağlanabilir.
*****************************     
CEVAP :  legal
*****************************

int foo()
{
    return 1;
}
    
int main()
{    
    int&& r = foo();
}    
*****************************     
AÇIKLAMA : life extension
*****************************     
CEVAP :  legal
*****************************

struct Data {
    int x, y;
    const int& cval;     // const L value
    int&& rval;          // R value
};  
*****************************     
AÇIKLAMA : her iki reference türü de R value referansa bağlanabilir.
normalde R value expression'a bağlandığında life-extension'a neden olur.
*****************************  

struct Data {
    int x, y;
    const int& cval;     // const L value
    int&& rval;          // R value
};
   
int foo(){ return 1; }
int bar(){ return 2; }
 
int main()
{    
    Data d = { 3, 5, foo(), bar() };
    d.cval;
}   
*****************************     
AÇIKLAMA : bu durumda da life extension geçerlidir.
d hayatta olduğu sürece d'nin cval ve rval elemanları ile oluşturulan bu geçici nesnelere erişim sağlanabilir.
*****************************     
CEVAP : legal
*****************************

  • direct initailization life extension'ı engeller.

    • direct initialization C++17 de doğrudan syntax error, C++20 de tanımsız davranıştır.

    • agreagte türlerin direct initialization'ı zaten c++20 ile dile eklendi.

struct Data {
    int x, y;
    const int& cval;     // const L value
    int&& rval;          // R value
};
   
int foo(){ return 1; }
int bar(){ return 2; }
 
int main()
{    
    Data d(3, 5, foo(), bar());
    d.cval;  // U.B
}    
*****************************     
AÇIKLAMA : direct initailze edersek üstteki kurallar geçerli değildir.
*****************************     
CEVAP : c++17 error c++20 tanımsız davranıştır.
*****************************

aggregate type için Nested type olabilir.
struct Data {
    int a, b;

    struct Nested {
        int x, y;
    }nst;
};
 
int main()
{    
    Data d = { 1, 3, { 5, 8 } };     // legal
    Data d1 = { 1, 3, 5, 8 };        // legal
    Data d2 = (1, 3, { 5, 8 });      // error
    Data d3 = (1, 3, (5, 8));        // cannot convert from initializer list to Data
    Data d4 = {1, 3, (5, 8)};        // legal
}  

struct Data {
    int a, b;

    struct Nested {
        int x, y;
    }nst;
};
 
int main()
{    
    Data d = {1, 3, (5, 8)};        // legal

    std::cout << d.nst.y << "\n";
}    
*****************************     
AÇIKLAMA : 8 yerine 0 yazdı.
bu şekilde struct Nested elemanına 8 ile ilk değer vermiş olmayız. (5, 8) virgül operatoru ile oluşturulan bir expression ve değeri 5 ve bunu x değişkenine ilk değer vermiş oluruz.
*****************************     
CEVAP : 0
*****************************

struct Data {
    int a, b;

    struct Nested {
        int x, y;
    }nst;
};
 
int main()
{    
    Data d = {1, 3, (5, 8), (4, 9)};        // legal

    std::cout << d.nst.x << "\n";
}
*****************************     
AÇIKLAMA : 
*****************************     
CEVAP : 8
*****************************

struct Data {
    int a, b;

    struct Nested {
        int x, y;
    }nst;
};
 
int main()
{    
    Data d = {1, 3, (5, 8), (4, 7)};        // legal

    std::cout << d.nst.y << "\n";
}    
*****************************     
AÇIKLAMA : 
*****************************     
CEVAP : 7
*****************************

struct Data {
    int a, b, c;
};
 
int main()
{    
    Data d1{ 1, 2, 3 };        // valid c++17/20
    Data d2(1, 2, 3);          // c++17 invalid, c++20 valid

    Data d3{ 1, 2.2, 3 };      // invalid( narrowing conversion )
    Data d4(1, 2.2, 3);        // warning( narrowing conversion )

    Data d5(.b = 6);           // invalid c++17/20
    Data d6{ .b = 6 };         // c++20/c++17 valid
}

  • designated initializer direct initializaton durumunda çalışmaz.


struct Data {
    int a, b, c;
};
 
int main()
{    
    Data d{ .a = 1, .c = 5 };     
}
*****************************     
AÇIKLAMA : 
*****************************     
CEVAP : legal
*****************************

struct Data {
    int a, b, c;
};
 
int main()
{    
    Data d(.a = 1, .c = 5);     
}
*****************************     
AÇIKLAMA : error: expected primary-expression before ‘.’ token
*****************************     
CEVAP : syntax error
*****************************

 
 
 

Recent Posts

See All
C++ Dilinde [[nodiscard]] attribute

C++’ta [[attribute]] (öznitelik) mekanizması, kod hakkında ek bilgi sağlayarak derleyicinin uyarılar vermesine, belirli optimizasyonlar...

 
 
 
C++ Dilinde constinit

C++20'de tanıtılan önemli bir özellik `constinit`'dir; bu özellik, statik depolama süresine sahip değişkenlerin derleme zamanında...

 
 
 

Comentarios


bottom of page