hot c++: new style of arguments passing
TRANSCRIPT
Version of 2015.07.10-2
Hot C++: New Style of Arguments PassingAndrey Upadyshev
Licensed under CC BY-SA 4.0 License1
Move Semantics Changes It All
✤ Moving is faster than copying and we want this performance gain!
✤ Template code has perfect forwarding, but non-template has nothing.
✤ We need to consider the best way to pass a movable object into a non-template function.
2
Problem
Typical function:void foo(const std::string& arg);
OK if foo implementation does not copy argument internally.
But if the implementation needs to copy argument, it loses the opportunity to move from user provided rvalue and does a copy anyway.
3
Straightforward Solution
Two overloads:void foo(const std::string& arg) { std::string t(arg); // copy ...} void foo(std::string&& arg) { std::string t(std::move(arg)); // move ...}
Not very elegant though: two near identical implementations of the same thing.
4
Problem Goes Deeper
struct Bar { Bar(const std::string& a, const std::string& b, const std::string& c) : a_(a), b_(b), c_(c) {}private: std::string a_, b_, c_;};
Straightforward solution is 2^3 overloads (2^N for general case). Too many, isn't it?
5
"Pass By Value" Solution
Foo::Foo(std::string arg): m_arg(std::move(arg)){}
Main point:1. If called with rvalue then it is moved into argument
else (i.e. called with lvalue) one is copied into argument.
2. Argument is moved further.
6
When Rvalue Passed
Foo::Foo(std::string arg): m_arg(std::move(arg)){}
std::string readFile();Foo foo1(readFile());
One move (thanks to copy elision)
std::string bar;...Foo foo2(std::move(bar));
Two moves
[Just] one extra move in the worst case comparing to straightforward solution.
7
When Lvalue Passed
Foo::Foo(std::string arg): m_arg(std::move(arg)){}
const std::string& bar();Foo foo(bar());
One copy and one move
[Just] one extra move in the worst case comparing to straightforward solution.
8
Good Expansibility
struct Bar { Bar(std::string a, std::string b, std::string c) : a_(std::move(a)) , b_(std::move(b)) , c_(std::move(c)) {}private: std::string a_, b_, c_;};
It’s always a single function regardless the number of arguments.
9
Pass By Value, Return By Value
Vector sorted(Vector arg) { std::sort(arg.begin(), arg.end()); return arg;}
“Pass by value” approach is seamlessly works with “return by value” one (Latter is a default one for C++11):
10
⬅ arg is moved on return.Note that RVO can’t be applied to a function argument
Pass By Value Is Not Perfect
✤ Not a “Swiss knife” like perfect forwarding: applicable only to types that are copyable and cheap movable.
✤ Even one extra move is not always acceptable
✤ Interface of the function depends on implementation.✤ Solution: pass by value if function conceptually needs to make a copy.
✤ Not to be used with base type because of slicing.
✤ Unnecessary copies if function needs a copy not every time. (Perhaps, such function asks for the refactoring :)
✤ Strange exception guarantee: a function itself is non-throw (move usually does not throw) but a function call throws because of argument copy.
11
Pass By Value Is Not Perfect
✤ Fresh copy + move assignment [1] can be significantly more expensive than pass by ref + copy assignment [2]. (Think about copy assigning to string that already has a large enough buffer allocated):
void Foo::setName(std::string name) { // [1] m_name = std::move(name); // [Always] destroy existing // string during move} foo.setName(meow); // [Always] allocate and copy bytes
vsvoid Foo::setName(const std::string& name) { // [2] m_name = name; // If existing string is big enough, // just copy bytes w/o realloc} foo.setName(meow); // Do nothing
12
Pass By Value Is Not Perfect, But…
✤ Nearly as efficient as pass by reference
✤ Easier to implement comparing to both other solutions (set of overloaded functions and template function accepting universal references)
✤ Generates less object code (arguable, depends on inlining)
✤ Despite it’s drawbacks which need to be always considered, it is a worth to use solution.
13
The Algorithm
Does the [non-template] function conceptually make a copy of a
[movable] argument?
Pass by const reference
Pass by value
Yes
No
Based on the profiler result or other evidence, optimize by providing
lvalue/rvalue overloads.
14
Useful Links
Boris Kolpackov, Efficient argument passing in C++11http://codesynthesis.com/~boris/blog/2012/06/19/efficient-argument-passing-cxx11-part1/
Dave Abrahams, Want Speed? Pass by Value.http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/
Scott Meyers, Effective Modern C++, Item 41
15
Discussion?
16