7 Eylül 2020 Pazartesi

std::tuple - Çok Sayıda Parametre Taşıyabilir

Giriş
std::pair ve std::tuple birbirlerine çok benziyorlar ancak std::tuple<A,B> ile std::pair<A,B> farklı tiplerdir.

std::pair ve std::tuple STL ile mecburen kullanılıyor çünkü algoritmaların genel (generic) olması gerekiyor. Kendi kodumda ise, std::pair ve std::tuple yerine daha anlamlı struct veya class'lar kullanmayı tercih ediyorum.

Şu satırı dahil ederiz.
#include <tuple>
constructor - brace initialization
İmzası şöyle. Direct constructor deniliyor.
tuple(const Types&... args );
Bir başka imzası şöyle. Aralarındaki farkı anlamadım
template< class... UTypes >
tuple( UTypes&&... args );

Örnek
Elimizde şöyle bir kod olsun
struct ListNode {
  int val;
  ListNode *next;
  ListNode(int x) : val(x), next(NULL) {}
};
tuple şöyle ilklendirilir.
ListNode node(0);
std::tuple<int, ListNode*> head{0, &node};
Yani brace initialization kullanılıyor.

constructor - std::make_tuple
Şöyle yaparız.
std::tuple<int, float, double> t = std::make_tuple(1, (float)1.2, 5);
constructor explicit tanımlıdır
Constructor şu anda explicit tanımlı. C++17 ile sadece parametre explicit tanımlı class ise constructor explicit olacak hale gelecek. Dolayısıyla std::initializer_list ile kullanırken şu anda std::tuple şeklinde yazmak gerekiyor. Elimizde şöyle bir metod olsun.
void ext( std::initializer_list<std::tuple<double, std::vector<double> >> myList )
{...}
Bu metodu şöyle çağırırız.
ext( { std::tuple<double,std::vector<double>>{1.0, {2.0, 3.0, 4.0} } } );
operator ==
tuple içindeki elemanları teker teker karşılaştırır.
Örnek
Elimizde şöyle bir kod olsun
auto t1 = std::make_tuple("one", "two", "three");
auto t2 = std::make_tuple("one", "two", "three");
    
std::cout << "(t1 == t2) is " << std::boolalpha << (t1 == t2) << "\n";
std::cout << "(t1 != t2) is " << std::boolalpha << (t1 != t2) << "\n";
Çıktı olarak şunu alırız. İki tuple nesnesinin eşit olmamasının sebebi içlerindeki veri aynı olsa bile pointer'ların farklı bellek alanlarını göstermesi.
(t1 == t2) is false
(t1 != t2) is true
operator =
İmzası şöyle. std::tie ile kullanabilmeyi sağlar.
template< class... UTypes >
tuple& operator=( const tuple<UTypes...>& other );

Diğer 
destructor sırası
Tuple içindeki elemanların hangi sıra ile yok edileceği belli değildir.

Kalıtım
Bu niye gerekir bilmiyorum ama std::tuple'dan kalıtmak mümkün. Şöyle yaparız
struct MyIntTuple : std::tuple<int> { 
  using std::tuple<int>::tuple;
};
sizeof
std::tuple sizeof() işlemine sokulunca tam anlamıyla optimize edilmiyor. Açıklaması şöyle.
I've checked all major compilers, and sizeof(std::tuple<int, char, int, char>) is 16 for all of them. Presumably they just put elements in order into the tuple, so some space is wasted because of alignment.

If tuple stored elements internally like: int, int, char, char, then its sizeof could be 12.

Is it possible for an implementation to do this, or is it forbidden by some rule in the standard?
is_trivial
std::tuple trival değildir. Şöyle yaparız
std::cout << std::is_trivial<std::tuple<int>>::value << std::endl;
Çıktı olarak şunu alırız.
0
is_trivially_copyable
std::tuple trivally copyable değildir. Şöyle yaparız
std::cout << std::is_trivially_copyable<std::tuple<int>>::value << std::endl;
Çıktı olarak şunu alırız.
0
std::make_tuple
Şöyle yaparız
using namespace std;

int main(){

  vector<tuple<int,int>> v;

  for (int var = 0; var < 100000000; ++var) {
    v.push_back(make_tuple(var, var));
  }
}
make_tuple normalde hep value döndürür.
int x, int y;
tuple<int,int> = make_tuple (x,y);
Ancak ref döndürmesini istersek std::ref ile kullanmak gerekir.
int x, int y;
tuple<int&,int> = make_tuple (std::ref(x),y);
std::get
std::get yazısına taşıdım.

std::tie
std::tie yazısına taşıdım.

Hiç yorum yok:

Yorum Gönder