C++ Dilinde std::any
- Yusuf Hançar
- Sep 16, 2024
- 5 min read
class any; (since C++17)
std::any, C++ dilinde genel veri taşıyıcı olarak kullanılan bir sınıftır. Bu, herhangi bir türdeki veriyi kapsülleyebilir ve bu veriye tür bilgisi kaybolmadan erişim sağlar. std::any, std::optional ve std::variant gibi bir vocabulary type olarak kabul edilir, yani genellikle türü değiştirmek veya tür bilgisi üzerinde işlem yapmak amacıyla kullanılır.
Özellikler
std::any, bir sınıf şablonu değil, doğrudan any adını taşıyan bir sınıftır. Bu, onu kullanmanın ve tür güvenliğini sağlamanın daha kolay ve doğrudan bir yolunu sunar.
Saklanan tür bilgisi kaybolmadan veriyi tutar. Ancak, saklanan türü bilmediğinizde veya yanlış türle erişmeye çalıştığınızda, tür dönüşümü işlemleri sırasında hata alabilirsiniz.
Veriye erişmek için "std::any_cast" fonksiyonunu kullanabilirsiniz. Bu fonksiyon, saklanan tür ile uyumlu bir türde veri döndürür veya tür uyumsuzluğu durumunda "std::bad_any_cast" sınıfı türünden exception throw eder.
#include <any>
int main ()
{
std::any a; // default initialization
std::any b{}; // value initialization
}
Doğrudan bir değerle initialize edilebilir.
#include <any>
int main ()
{
std::any val(5);
std::any val1(5.4);
std::any val2("smartcode");
std::any val3("smartcode"s);
std::any val4(vector<int>{2, 4, 6, 8 });
}
Run-time da değeri degistirilebilir.
void* türüne belirli senaryolar hariç ciddi senaryolar oluşturur.
void* da herhangi bir türden nesnenin adresini tutabiliriz. Bu void* yoluyla da o adresteki nesneye erişebiliriz. Ancak bu durumda nesnenin tür bilgisini kaybederiz.
std::any void* gibi herhangi bir nesnenin adresini tutabilir ancak tür bilgisini de tutar.
#include <any>
int main ()
{
std::any val(5);
val = "smartcode";
}
std::any sınıfının construct edeceği nesneyi kendi storage'ı içinde tutma zorunluluğu yoktur. Tamamen implementasyona bırakılmıştır.
Storage değeri düşük nesneleri kendi içindeki buffer alanında tutmaya yöneltir ancak zorunlu tutmaz. Büyük nesneler için dinamik bir bellek alanı tahsis edebilir.
Bu açıdan bakınca std::any sınıfının maliyet faktörlerinden biri sizeof() değeri büyük olan nesneler için dinamik bellek alanı allocation durumudur.
#include <any>
int main ()
{
std::cout << "sizeof(std::any) : " << sizeof(std::any) << "\n";
}
*****************************
CEVAP : 16
*****************************
std::any nesnenin türünü void* dan farklı olarak nasıl tutar ?
type_info nesnesine reference tutmasıdır. Nesnenin türü de bu sayede elde edilir. (run-time type identification ve typeid operator hatırlanmalıdır)
typeid operatorü dynamic_cast operatorunden farklı olarak polymorphic türlerde kullanılmak zorunda değildir.
std::any tuttuğu type nasıl öğrenilir.
type()
geri dönüş değeri "const type_info&"
int main ()
{
std::any val(10);
if (val.type() == typeid(int))
{
std::cout << "int deger\n";
}
else if (val.type() == typeid(std::string))
{
std::cout << "string deger\n";
}
}
std::any nesnesi boş ise type() fonksiyonu void tutar.
int main ()
{
std::any an;
std::cout << std::boolalpha << (an.type()== typeid(void));
}
*****************************
CEVAP : true
*****************************
Atama durumunda yine decay gerçekleşir.
#include <iostream>
#include <any>
int main ()
{
int arr[10]{}; // type is int[10]
std::any an = arr; // convert to int*
std::cout << std::boolalpha << (an.type()== typeid(int[10])) << "\n";
std::cout << std::boolalpha << (an.type()== typeid(int*));
}
*****************************
AÇIKLAMA : any pointer to int değer tutar.
array pointer'a dönüştürülürken dizinin boyutu kaybolur ve sadece pointer olarak değerlendirilir.
*****************************
CEVAP :
false
true
*****************************
has_value()
bool has_value() const noexcept;
#include <any>
int main ()
{
std::any val;
std::boolalpha(cout);
std::cout << "val.has_value() : " << val.has_value() << "\n";
}
*****************************
CEVAP : false
*****************************
std::any tuttuğu değere erişmek için "std::any_cast" function template kullanılır.
int main ()
{
std::any val{ 10 };
std::cout << any_cast<int>(val);
}
*****************************
CEVAP : 10
*****************************
#include <iostream>
#include <any>
int main ()
{
std::any val{ 10.4 };
try
{
std::cout << std::any_cast<int>(val);
}
catch (const std::exception& ex)
{
std::cout << "caught : " << ex.what() << "\n";
}
}
*****************************
AÇIKLAMA : double değer tutup int çekmeye çalışırsak exception throw eder.
*****************************
CEVAP : caught : bad any_cast
*****************************
int main ()
{
std::any val{ 10 };
std::any_cast<int>(val) = 12;
}
*****************************
AÇIKLAMA : cast işlemi int türüne yapıldı. Bu reference türü değildir. Pr value.
*****************************
CEVAP : syntax error
lvalue required as left operand of assignment
*****************************
int main ()
{
std::any val{ 10 };
std::any_cast<int&>(val) = 12;
}
*****************************
AÇIKLAMA : std::any türünde bir değerin referansla değiştirilmesine izin veren std::any_cast fonksiyonunun varlığıdır. Ancak, referans ile değişiklik yapabilmeniz için val içindeki değerin uygun türde (int) olması gerekmektedir. Eğer val başka bir tür içeriyorsa, bu kod bir istisna fırlatır ve çalışma zamanında bir hata ile karşılaşırsınız.
*****************************
CEVAP : legal
*****************************
std::in_place_type
Birden fazla arguman ile std::any nesnesinin sağladığı bellek alanında nesneyi construct etmek için kullanılır.
#include <iostream>
#include <any>
#include <vector>
std::vector<std::string> all_month = {"Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"};
std::vector<std::string> all_day = {"Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"};
class Date {
public:
Date(int day, int month, int year) : m_day(day), m_month(month), m_year(year) {}
std::string convert_to_date() const
{
std::string dname = all_day[get_day_of_week()];
std::string mname = all_month[m_month - 1];
return std::to_string(m_day) + " " + mname + " " +
std::to_string(m_year) + " " + dname;
}
int get_day_of_week() const
{
int day = (14 - m_month) / 12;
int year = m_year - day;
int mnt = m_month + 12 * day - 2;
return (m_day + year + year / 4 - year / 100 + year / 400 + (31 * mnt) / 12) % 7;
}
private:
int m_day;
int m_month;
int m_year;
};
std::ostream& operator<<(std::ostream& os, const Date& date) {
os << date.convert_to_date();
return os;
}
int main()
{
std::any val{ std::in_place_type<Date>, 29, 5, 1994 };
std::cout << std::any_cast<Date>(val);
return 0;
}
*****************************
CEVAP : 29 Nisan 1994 Cuma
*****************************
std::make_any
template <class T, class... Args>
std::any make_any(Args&&... args);
int main ()
{
std::cout << std::any_cast<Date>(std::make_any<Date>(29, 5, 1994));
}
#include <iostream>
#include <any>
#include <string>
int main ()
{
std::cout << std::any_cast<std::string>(std::make_any<std::string>(5, '+'));
}
*****************************
CEVAP : +++++
*****************************
reset()
void reset() noexcept;
#include <iostream>
#include <any>
#include <string>
int main ()
{
auto val = std::make_any<std::string>(5, '+');
std::cout << std::boolalpha << val.has_value() << "\n";
val.reset();
std::cout << std::boolalpha << val.has_value() << "\n";
}
*****************************
CEVAP :
true
false
*****************************
std::any::emplace
template <class ValueType, class... Args>
std::decay_t<ValueType>& emplace(Args&&... args);
int main ()
{
auto val = std::make_any<std::string>(5, '+');
std::cout << std::boolalpha << val.has_value() << "\n";
val.reset();
std::cout << std::boolalpha << val.has_value() << "\n";
val.emplace<std::string>("smartcode");
std::cout << std::boolalpha << val.has_value();
}
*****************************
CEVAP :
true
false
true
*****************************
Taşıma semantik ilişkisi...
#include <iostream>
#include <any>
#include <string>
#include <type_traits>
int main()
{
std::any an{ std::string{ "cpp14" } };
std::cout << std::any_cast<std::string>(an) << "\n";
auto& val = std::any_cast<std::string&>(an);
val[4] = '7';
auto str = std::any_cast<std::string>(std::move(an));
static_assert(std::is_same_v<decltype(str), std::string>);
std::cout << "str.size() : " << str.size() << "\n";
std::cout << "an.size() : " << std::any_cast<std::string>(&an)->size() << "\n";
std::cout << "str : " << str << "\n";
}
*****************************
AÇIKLAMA : an degiskeni moved-from state ve size degeri 0 olur.
*****************************
CEVAP :
cpp14
str.size() : 5
an.size() : 0
str : cpp17
*****************************
Implementations are encouraged to avoid dynamic allocations for small objects, but such an optimization may only be applied to types for which std::is_nothrow_move_constructible returns true.
std::is_nothrow_move_constructible, bir türün taşınabilirken (move construction) exception throw etmeden gerçekleştirilip gerçekleştirilemeyeceğini belirten bir tür özelliğidir. Eğer bu tür true ise, taşınabilirlik sırasında exception throw atılmaz; eğer false ise, taşınabilirlik sırasında exception throw edilir.
#include <array>
#include <type_traits>
#include <iostream>
struct Data {
int data;
Data(int d) : data(d) {}
Data(Data&&) noexcept = default; // noexcept(true)
Data(Data&&) noexcept(false) = default;
};
static_assert(std::is_nothrow_move_constructible<Data>::value, "Data exception atabilir");
void func(Data val) {}
int main()
{
Data val(10);
func(std::move(val));
return 0;
}
Comments