C++ Dilinde Deduction Guide
- Yusuf Hançar
- Oct 13, 2024
- 6 min read
C++ dilinde template argument deduction, derleyicinin fonksiyon argümanlarından template parametrelerini çıkarmasına olanak tanır. Bu, kodu daha temiz ve okunabilir hale getirir. C++20 ile birlikte kullanıcı tanımlı deduction guide'ların (çıkarma kılavuzları) tanıtılması, geliştiricilere template parametrelerinin nasıl çıkarılacağını kontrol etme imkanı sunmuştur.
Deductions Guide Nedir?
Deduction guide, derleyiciye class template için argümanlarının nasıl çıkarılacağına dair özel bir yapı sunar. Bu, bir constructor parametreleri ile class template yapılarının parametreleri(template parameters) arasındaki ilişkileri belirler. Deductions guide'lar ya otomatik olarak oluşturulabilir ya da kullanıcı tarafından tanımlanabilir.
Otomatik Olarak Oluşturulan Deductions Guide
Bir template class, argümanlarını belirtmeden başlatıldığında, derleyici constructor'lara dayalı olarak çıkarım kılavuzları oluşturabilir. İşleyiş şekli şu şekildedir:
Constructor Templates:
Class template C'nin her constructor'ı C_i için, F_i adında fictional bir function template oluşturulur. F_i'nin template parametreleri, hem C hem de C_i'nin template parametrelerini içerir. (cppreference örnekleriyle anlatılmaktadır)
Kopya Deductions Candidate:
Aynı türdeki mevcut bir nesneden başlatma yapabilmek için bir kopya çıkarım adayı da oluşturulur.
Aggregate Types:
Eğer class template bir aggregate türü gibi davranıyorsa ve kullanıcı tanımlı deduction guide'ı yoksa, sağlanan başlatma listesine dayalı olarak bir aggregate deduction adayı eklenebilir.
Bu kılavuzlar, derleyicinin constructor parametrelerini otomatik olarak template parametreleriyle eşleştirmesine olanak tanır, böylece sınıf başlatma süreci daha akıcı hale gelir.
Kullanıcı Tanımlı Deductions Guide
Kullanıcı tanımlı deduction guide'lar, geliştiricilerin template argümanlarının nasıl çıkarılacağını özelleştirmelerine olanak tanır. Aşağıdaki gibi tanımlanırlar:
template <typename... Args>
ClassTemplate(Args...) -> ClassTemplate<ExpectedTemplateArguments>;
Bu yapı, constructor argümanlarından hangi template parametrelerinin çıkarılacağını açıkça kontrol etmeyi sağlar. Özellikle default template parametreleri veya ek kısıtlamalar gerektiğinde kullanışlıdır.
Deductions Guide Oluşturma Rehberi
No Placeholder Types:
Deductions guide'ların parametrelerinde placeholders types olamaz, bu da beklenen türlerin net bir şekilde belirtilmesini sağlar.
Access ve Scope:
Deductions guide'lar, ilgili class template ile aynı scope içinde tanımlanmalıdır, bu da uygun erişim kontrolünü sağlar.
Implicit vs. User-Defined :
Her iki tür kılavuz da mevcut olduğunda, kullanıcı tanımlı deduction guide'lar, overload resolution da otomatik olarak oluşturulanlardan daha fazla önceliğe sahiptir.
template <typename T1, typename T2>
struct Pair {
T1 first;
T2 second;
Pair(T1 f, T2 s) : first(f), second(s) {}
};
// User-defined deduction guide
template <typename T1, typename T2>
Pair(T1, T2) -> Pair<T1, T2>;
auto p = Pair{1, 2.5}; // Pair<int, double>
Şimdi örneklerle bunu pekiştirelim :
template <typename>
class TypeTeller;
template <typename T>
class Data {
public :
Data(const T& r) : mx{ r }{}
private :
T mx;
};
// deduction guide
template <typename T>
Data(T)->Data<T>;
int main ()
{
int arr[40]{};
Data d(arr);
}
*****************************
AÇIKLAMA : T türü int* olarak yapılır.
*****************************
template <typename>
class TypeTeller;
template <typename T>
class Data {
public :
Data(T)
{
TypeTeller<T> tt;
}
};
// deduction guide
// ctor'a T turunden deger gonderince template parametresi sol taraf degeri olacaktir.
template <typename T>
Data(T)->Data<T&>;
int main ()
{
Data d(10);
}
*****************************
AÇIKLAMA :
deduction guide devreye girdi. T türünün cikarimi dogru şekilde "int&" olarak yapiilir.
Ancak gonderilen arguman R value oldugu için syntax error.
*****************************
CEVAP : syntax error
*****************************
int main ()
{
int val{10};
Data d(val);
}
*****************************
AÇIKLAMA : L value gonderdik
d -> Data<int&>
*****************************
CEVAP : legal
*****************************
template <typename T>
class Data {
public :
Data(T) {}
};
Data(char)->Data<long>;
Data(short)->Data<long>;
Data(int)->Data<long>;
Data(unsigned)->Data<long>;
int main ()
{
Data d1(5.6); // d1 -> Data<double>
Data d2('A'); // d2 -> Data<long>
Data d3(25u); // d3 -> Data<long>
Data d4(25L); // d4 -> Data<long>
}
*****************************
AÇIKLAMA : user defined deduction guide
*****************************
C++20 standartları ile gelen concept ile deduction guide kullanılabilir.
template <typename T>
class Data {
public :
Data(T) {}
};
// sadece integral türler için devreye girecek bir concept
template <std::integral T>
Data(T)->Data<unsigned long long>;
int main ()
{
Data d1(5.6);
Data d2(25LL);
Data d3(25u);
Data d4(25);
}
*****************************
AÇIKLAMA : concecpt ile artık tüm tam sayı türler için deduction guide devreye girmesini sağladık.
*****************************
template <typename T>
class Data {
public :
Data(T){}
};
// argüman pointer türü olmasa da çıkarım pointer türü olarak yapılır.
template <typename T>
Data(T)->Data<T*>;
int main ()
{
Data d(10);
}
*****************************
AÇIKLAMA : T türü int* olur ve gönderilen bir pointer değil.
int to int* conversion error.
*****************************
CEVAP : syntax error
*****************************
template <typename T>
class Data {
public :
Data(T){}
};
Data(const char*)->Data<std::string>;
int main ()
{
Data d{ "smartcode" };
}
*****************************
AÇIKLAMA : deduction guide olmasaydı T türü const char* olacaktı.
ancak şu an std::string olarak çıkarım yapacaktır.
*****************************
Yukarıda da bahsettiğimiz gibi std::pair veya std::tuple bu mantıkla çalışır.
template <typename T, typename U>
class Pair {
public :
Pair (const T&, const U&){}
};
template <typename T, typename U>
Pair(T, U)->Pair<T, U>;
int main ()
{}
*****************************
AÇIKLAMA : bu sayede fonksiyonun parametresi reference olmasına rağmen dizi parametre olarak geçildiğinde T türünün çıkarımı dizi olarak değil pointer türü olarak yapılır.
*****************************
template <typename... Types>
class Tuple {
public :
constexpr Tuple(const Types&...); // takes arguents by reference
template <typename... UTypes> constexpr Tuple(UTypes&&...);
// ...
};
template <typename... Types>
Tuple(Types...)->Tuple<Types...>; // deduce argument types by-value
int main ()
{
Tuple tx{ 20, "cpp", nullptr };
}
*****************************
AÇIKLAMA : parametre geçilenlerin dizi olarak değilde pointer olarak çıkarımının yapılması sağlandı.
*****************************
#include <iostream>
using namespace std;
template <typename T>
struct Data {
T val;
// ctor parametreleri universal ref.
template <typename... Types>
Data(Types&&... vals) : val{ (vals + ...) }{} // burda fold expression vardır.
};
// deduction guide
template <typename... Types>
Data(Types&&... ts)-> Data<std::common_type_t<Types...>>;
int main ()
{
Data s{ 1u, 2.0, 3, 4.0f };
Data strsun{ std::string{ "abc" }, "def" };
cout << s.val << endl << strsun.val << endl;
}
*****************************
AÇIKLAMA :
common_type_t bir meta function
*****************************
CEVAP :
10
abcdef
*****************************
deduction guide template olmak zorunda değildir.
template <typename T>
struct Data {
T str;
};
Data(const char*)->Data<std::string>;
int main()
{
Data my{ "smart code" };
std::cout << boolalpha << (typeid(my) == typeid(Data<std::string>));
}
template <typename T>
struct Data {
T str;
};
int main()
{
Data<int> d = 10;
}
*****************************
AÇIKLAMA : agregate için böyle bir initialize yok.
*****************************
CEVAP : syntax error - conversion from ‘int’ to non-scalar type ‘Data’ requested
*****************************
template <typename T>
struct Data {
T str;
};
int main()
{
Data<int> d1(10);
Data<int> d2{10};
Data<int> d3 = { 10 };
}
template <typename T>
struct Data {
T str;
};
int main()
{
Data my(10);
}
*****************************
AÇIKLAMA : C++17 -> no matching function for call to ‘Data(int)’
C++20 -> legal
*****************************
CEVAP :
*****************************
template <typename T>
struct Data {
T str;
};
template <typename T>
Data(T)->Data<T>;
int main()
{
Data d = { 10 };
}
*****************************
AÇIKLAMA : c++17
aggregate için c++17 de gerekli, c++20 de gerek yoktur.
*****************************
CEVAP : legal
*****************************
template <typename T>
class Data {
public :
Data(T param) : mx{ param }{}
private :
T val;
};
template <typename T>
Data(T)->Data<T>;
int main()
{
Data x{5}; // Data<int>
Data y{5.4}; // Data<double>
auto z = Data{ 4L }; // Data<long>
Data* p = &x; // geçersiz
Data n1{ 'A' }, n2{ 23 }; // geçersiz
}
template <typename T>
class Data {
public :
Data(T param) : mx{ param }{}
private :
T val;
};
int main()
{
Data d(12);
Data* ptr = &d;
}
*****************************
AÇIKLAMA : template placeholder type ‘Data<...auto...>’ must be followed by a simple declarator-id
*****************************
CEVAP : syntax error
*****************************
deduction guide explicit keyword ile bildirilebilir.
template <typename T>
struct Data {
T val;
};
explicit Data(const char*)->Data<std::string>;
int main()
{
Data m1 = { "cpp" }; // geçersiz, deduction guide ignored
Data m2("cpp"); // geçersiz
Data m3 = Data("cpp"); // geçerli
Data m4 = { Data{"cpp"} }; // geçerli
}
*****************************
AÇIKLAMA :
*****************************
CEVAP : syntax error
*****************************
template <typename T>
struct Data {
Data(T) { cout << "Data(T)" << endl; }
template <typename U>
Data(U) { cout << "Data(U)" << endl; }
};
template <typename T>
explicit Data(T)->Data<T*>;
int main()
{
Data p1{ 42 }; // Data<int*> deduction guide
Data p2 = 42; // Data<int> ctor // copy init old. için deduction guide explicit olduğundan devre dışı kalır.
int idx = 10;
Data p3{ &idx }; // Data<int**> deduction guide
Data p4 = &idx; // Data<int*> ctor
}
*****************************
AÇIKLAMA :
*****************************
CEVAP : syntax error
*****************************
template <typename T>
struct Data {
Data(T) { cout << "Data(T)" << endl; }
};
template <typename T>
explicit Data(T)->Data<T*>;
int main()
{
int ival{ 10 };
Data d(ival);
}
*****************************
AÇIKLAMA : deduction guide ile T int* oldu. Gönderdiğimiz adres olmadığı için hata
*****************************
CEVAP : syntax error
*****************************
#include <iostream>
using namespace std;
template <typename T>
struct Data {
Data(T) { cout << "Data(T)" << endl; }
template <typename U>
Data(U) { cout << "Data(U)" << endl; }
};
template <typename T>
explicit Data(T)->Data<T*>;
int main()
{
int ival{ 10 };
Data d(ival);
}
*****************************
AÇIKLAMA : deduction guide devreye girmedi.
*****************************
CEVAP : Data(U)
*****************************

コメント