1

I used to pass every complex structure by const & or at least by &. But with the new std::move semantic and all the optimizations that compilers offer today, is it still the option to go?

Consider such example:

struct Task{
    unsigned timeMS;
    void(*function)(unsigned, unsigned) = 0;
    Task(unsigned timeMS, void(*function)(unsigned, unsigned)) 
        : timeMS(timeMS), function(function){}
};

class Timeline{
    std::vector<Task> tasks;
    ...
};

class App{
...
public:
    inline void addTask1(const Task &task){ timeline.add(Task); }
    inline void addTask2(Task &task){ timeline.add(Task); }
    inline void addTask3(Task task){ timeline.add(Task); }
};

Which one of addTask1, addTask2, addTask3 is the way to go? Assume that App::addTask() is an heavily used method.

I guess that const & requires to create a copy, but I've learned that things are not as simple as they look. It's enough to mention RVO (http://en.wikipedia.org/wiki/Return_value_optimization) - and I'm sure that there are much more things that should be taken into account (and I'm not aware of them yet).

I know that inline is in fact just the suggestion for compiler, rather then an order. But does it change anything in const & vs & vs by value battle?

I am working with VC++ 2013, I'm not focused on gcc too much.

P.s. Note that App::addTask call Timeline::add which call vector::push_back. So the parameter is passed more then once - should I make both App::addTask and Timeline::add of the same "type" (const & vs & vs by value).

PolGraphic
  • 3,233
  • 11
  • 51
  • 108
  • Move helps not at all, bit I would be surprised if the `const&` or the value would compile to something different after inline. – Yakk - Adam Nevraumont Dec 25 '14 at 20:28
  • Member functions defined within a class definition are implicitly inline – Brian Bi Dec 25 '14 at 20:31
  • @Pradhan that question was precisely about the `std::string` and not about user-defined custom structures. I'm not convinced that it's 1:1 case (e.g. I guess that copy constructor cost is way different for `std::string`). – PolGraphic Dec 25 '14 at 20:32
  • @PolGraphic As the answer explains, if you will be making a copy of the argument, pass by value. If not, pass by `const &`. Also, if you will just be forwarding it elsewhere, you might want to look into the perfect forwarding idiom. – Pradhan Dec 25 '14 at 20:35

1 Answers1

3

Whether pass by value or const& is appropriate depends, obviously what's done next with the object. Especially when a copy of an object is needed in some form and the objects being passed are likely to originate from temporary objects, using pass by value is probably preferable: when passing by value the compiler is allowed to elide the copy. When passing by const& copy elision is not allowed (for the copy to be elided the compiler would need to prove that creating a copy doesn't have observable side-effects).

Of course, when passing by value the value happens to be an lvalue, i.e., to have it moved rather than copied, you'll need to std::move() it when passing on (the further copy can't be elided anyway):

void addTask3(Task task) { timeline.add(std::move(task)); }

BTW, you omitted the perfect forwarding version:

template <typename T>
void addTask4(T&& task) { timeline.add(std::forward<T>(task)); }

This version won't be allowed to elide copies, either, but it may have the advantage to create a more expensive version in-situ based on a suitable conversion.

I don't have any benchmarks to determine the difference, though. If someone could suggest a decent way to benchmark the differences I'm happy to add a corresponding benchmark to my suite of benchmarks and augment this answer with the results.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380