Calvin
Calvin

Reputation: 2912

Can I let the C++ compiler decide whether to pass-by-value or pass-by-reference?

Have a look at this hypothetical header file:

template <class T>
class HungryHippo {
public:
    void ingest(const T& object);
private:
    ...
}

Now, for a HungryHippo<string> it makes sense that you would want to ingest references to the strings -- copying a string might be very expensive! But for a HungryHippo<int> it makes way less sense. Passing an int directly can be really cheap (most compilers will do it in a register), but passing a reference to an int is an extra needless level of indirection. This all applies to returning values as well.

Is there some way to suggest to the compiler "hey, I'm not going to modify the argument, so you decide whether to pass by value or by reference, depending on what you think is better"?

Some things that may be relevant:

Upvotes: 15

Views: 711

Answers (2)

Matthew Hall
Matthew Hall

Reputation: 605

While tricks such as boost's mentioned call_traits<T> do what they claim to do in this case, I think you're assuming that the compiler is not already making this optimization in the most important cases. It is trivial, after all. If you accept a const T& and sizeof(T) <= sizeof(void*), the invariants imposed by C++ reference semantics allow the compiler to simply substitute the value throughout your function body if it's a win. If it's not, your worst-case overhead is one pointer-to-arg dereference in the function prologue.

Upvotes: 0

Darren Engwirda
Darren Engwirda

Reputation: 7015

The boost::call_traits header deals with exactly this issue. Check it out here.

Specifically, the call_traits<T>::param_type option includes the following description:

If T is a small built in type or a pointer, then param_type is defined as T const, instead of T const&. This can improve the ability of the compiler to optimize loops in the body of the function if they depend upon the passed parameter, the semantics of the passed parameter is otherwise unchanged (requires partial specialization).

In your case, you could define ingest as follows:

template <class T>
class HungryHippo {
public:
    void ingest(call_traits<T>::param_type object);
    // "object" will be passed-by-value for small 
    // built-in types, but passed as a const reference 
    // otherwise
private:
    ...
};

Whether this would actually make much of a difference in your actual code/compiler combination, I'm not sure. As always, you'd have to run some actual benchmarks and see what happens...

Upvotes: 8

Related Questions