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).

4
  • Move helps not at all, bit I would be surprised if the const& or the value would compile to something different after inline. Commented Dec 25, 2014 at 20:28
  • Member functions defined within a class definition are implicitly inline Commented Dec 25, 2014 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). Commented Dec 25, 2014 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. Commented Dec 25, 2014 at 20:35

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.