Giriş
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::stringCopy 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_ptrCopy 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
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";
}