30 Kasım 2020 Pazartesi

signed Anahtar Kelimesi

Giriş
C++ ile signed anahtar kelimesini kullanmak çoğunlukla isteğe bağlı, çünkü bir integral tipin varsayılan tipi signed. Ancak iki yerde istisna var. Açıklaması şöyle
There are at least two places where the signed keyword is not a no-op:
1. char Tipi
 Açıklaması şöyle
With char: the signedness of "plain" char is implementation-defined. On implementations where it's an unsigned type, signed char is needed to get the signed variant. Even if char is a signed type, signed char, char, and unsigned char are all distinct types.
Yani sadece char olarak kullanırsak signed char veya unsigned char olabilir. Tamamen derleyiciye bağlı

2. Bitfield Tipi
Açıklaması şöyle
With bitfields: bitfield members without explicit signedness have implementation-defined signedness.
Bitfield için elimizde şöyle bir kod olsun. b alanı signed veya unsigned olabilir. Tamamen derleyiciye bağlı
struct foo {
  int b:1;
};
Açıklaması şöyle
b may be { -1, 0 } or { 0, 1 } depending on the implementation

Named Return Value Optimization (NRVO) - Constructor ve Destructor Metodları Çağrılmaz.

Giriş
Return Value Optimization (RVO) iki çeşittir.
1. Unnamed RVO
2. Named RVO

Named RVO Nedir?
Named RVO Copy Elision kuralının özel bir durumu. Bu kural sayesinde l-value bir nesne r-value bir nesneye dönüşür. 

CopyElision ve onun alt kümesi olan NRVO C++11 ve C++14 ile mecburi değil ancak C++17 ile mecburi hale geliyor.

NRVO'nun Etkisi Nedir?
NRVO uygulanırsa return edilen nesnenin copy/move constructor metodu çağrılmaz. Constructor çağrılmadığı için destructor'ı varsa o da çağrılmaz. Bu durum şaşırtıcı sonuçlara sebep olabiliyor. Açıklaması şöyle
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the constructor selected for the copy/move operation and/or the destructor for the object have side effects.
Örnek
NRVO'nun olduğunu görmek için şöyle bir kod olsun.
std::vector<int> foo() {
 int i;
 std::vector<int> a(100);
 printf("%p, %p, %p\n", &i, &a, &(a[0]));
 return a;
}

int main() {
 int i;
 std::vector<int> b = foo();
 printf("%p, %p, %p\n", &i, &b, &(b[0]));
}
Çalıştırmak için şöyle yaparız. Hem foo() metodundaki hem de main() metodundaki std::vector nesnesinin adresi aynıdır.
$ vim main.cpp 
$ cc -std=c++11 -lc++ main.cpp
$ ./a.out
0x7ffee28d28ac, 0x7ffee28d28f0, 0x7ff401402c00
0x7ffee28d290c, 0x7ffee28d28f0, 0x7ff401402c00
$ 
std::string
Copy constructor veya move constructor, destructor metodları copy elision sayesinde çağrılmaz.
std::string system_call(const char *cmd){
  std::string a;
  ...
  return a;
}

std::string st = system_call("whatever code"); //system_call metoduna direkt st geçilir
std::unique_ptr
Copy constructor metodu olmayan bir nesne olan unique_ptr kullanan bu kod, copy elision sayesinde derlenir.
#include <iostream>
#include <memory>

using namespace std;

unique_ptr<int> foo()
{
  unique_ptr<int> p( new int(10) );

  return p;                   // 1
  //return move( p );         // 2
}

int main()
{
  unique_ptr<int> p = foo();

  cout << *p << endl;
  return 0;
}
std::vector
Örnek
Şöyle yaparız.
std::vector<huge_thing> foo()
{
  std::vector<huge_thing> result{/* ... */};
  return result;
}

void bar()
{
  auto v = foo(); // (0)
}
Kendi Sınıfım
Örnek
Şöyle yaparız. Copy constructor delete olduğu halde derlenir
struct Foo {
    Foo() = default;
    Foo(const Foo&) = delete;
};

int main() {
  // Works in C++17 and C++20, fails in C++14 and before
  Foo foo = Foo(); 
}
Örnek
Şöyle yaparız. Copy constructor ve destructor çalışmaz
struct Foo {
  Foo() { std::cout << "Constructed" << std::endl; }

  Foo(const Foo &) { std::cout << "Copy-constructed" << std::endl; }

  Foo(Foo &&) { std::cout << "Move-constructed" << std::endl; }

  ~Foo() { std::cout << "Destructed" << std::endl; }
};

Foo foo() {
  Foo mystr;
  return mystr;
}

int main() {
  Foo result = foo();
}
Örnek
Şöyle yaparız. Burada NRVO uygulanırsa Foo sınıfının destructor metodu çalışmaz. Bu durumda main metodunun çıktısı 0 olur
// Foo adds an element to a std::vector passed by reference
// on construction in the destructor
struct Foo {
  Foo(std::vector<double>& v) : m_v(v){
  }
  ~Foo(){
    m_v.push_back(1.0);
  }
  std::vector<double>& m_v;
};

std::vector<double> bar(){
  std::vector<double> ret;
  Foo foo(ret);
  return ret;
}

int main(){
  std::cout << bar().size() << "\n";
}