16
Version of 2015.07.10-2 Hot C++: New Style of Arguments Passing Andrey Upadyshev Licensed under CC BY-SA 4.0 License 1

Hot C++: New Style of Arguments Passing

Embed Size (px)

Citation preview

Page 1: Hot C++: New Style of Arguments Passing

Version of 2015.07.10-2

Hot C++: New Style of Arguments PassingAndrey Upadyshev

Licensed under CC BY-SA 4.0 License1

Page 2: Hot C++: New Style of Arguments Passing

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

Page 3: Hot C++: New Style of Arguments Passing

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

Page 4: Hot C++: New Style of Arguments Passing

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

Page 5: Hot C++: New Style of Arguments Passing

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

Page 6: Hot C++: New Style of Arguments Passing

"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

Page 7: Hot C++: New Style of Arguments Passing

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

Page 8: Hot C++: New Style of Arguments Passing

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

Page 9: Hot C++: New Style of Arguments Passing

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

Page 10: Hot C++: New Style of Arguments Passing

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

Page 11: Hot C++: New Style of Arguments Passing

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

Page 12: Hot C++: New Style of Arguments Passing

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

Page 13: Hot C++: New Style of Arguments Passing

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

Page 14: Hot C++: New Style of Arguments Passing

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

Page 15: Hot C++: New Style of Arguments Passing

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

Page 16: Hot C++: New Style of Arguments Passing

Discussion?

16