Object Orientation vs. Performance¶

In [94]:
!rm -Rf tmp
!mkdir -p tmp

Copies, Copies Everywhere¶

In [95]:
%%writefile tmp/array.cpp

#include <iostream>

class array_t {
        float m_value;
    
    public:
        array_t(float value)
        : m_value(value)
        { std::cerr << "constructor" << std::endl; }
    
        array_t(const array_t &src)
        : m_value(src.m_value)
        { std::cerr << "copy constructor" << std::endl; }

        array_t operator+(array_t const &op2)
        { return array_t(m_value+op2.m_value); }

        array_t operator*(array_t const &op2)
        { return array_t(m_value*op2.m_value); }
};

int main()
{
    array_t x(5);
    array_t y(6);
    array_t z(7);
    
    std::cerr << "begin expression" << std::endl;
    array_t result(x+y*z+x*x);
}
Writing tmp/array.cpp
  • What does this print after begin expression?
  • Now disable -fno-elide-constructors and/or use C++17 (where URVO became mandatory).
  • Is this (naively) legal, i.e. is the obervable behavior the same as if there were no optimization?
  • What can wreck RVO?
In [7]:
!cd tmp; g++ -std=c++11 -O array.cpp -oarray -fno-elide-constructors
!tmp/array
constructor
constructor
constructor
begin expression
constructor
copy constructor
constructor
copy constructor
constructor
copy constructor
constructor
copy constructor
copy constructor

Move Semantics¶

In [8]:
%%writefile tmp/move.cpp

#include <string>
#include <iostream>


struct A
{
    std::string s;
    A() : s("test") { std::cout << "constructed\n"; }
    A(const A& o) : s(o.s) { std::cout << "copied\n"; }
    A(A&& o) noexcept : s(std::move(o.s)) { std::cerr << "moved" << std::endl; }
};
 
A f(A a)
{
    return a;
}
 
int main()
{
    A a1 = f(A());
    A a2 = std::move(a1);
}
Overwriting tmp/move.cpp
In [9]:
!cd tmp; g++ -O move.cpp -omove
!tmp/move
constructed
moved
moved
  • What does this print?
  • How does return value optimization factor into this?
In [ ]: