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:
template <class T, bool PassByValue> class HungryHippo
and then specializing on PassByValue
. If I wanted to get really fancy, I could even infer PassByValue
based on sizeof(T)
and std::is_trivially_copyable<T>
. Either way, this is a lot of extra work when the implementations are going to look pretty much the same, and I suspect the compiler can do a much better job of deciding whether to pass by value than I can.ingest
is fairly complicated and not worth inlining.inline
by default.Pass by Reference A reference parameter "refers" to the original data in the calling function. Thus any changes made to the parameter are ALSO MADE TO THE ORIGINAL variable. Arrays are always pass by reference in C.
C always uses 'pass by value' to pass arguments to functions (another term is 'call by value', which means the same thing), which means the code within a function cannot alter the arguments used to call the function, even if the values are changed inside the function.
When passing by reference you are basically passing a pointer to the variable. Pass by value you are passing a copy of the variable. In basic usage this normally means pass by reference, changes to the variable will seen be in the calling method and in pass by value they won't.
Always passing a reference and putting const in front of it is tedious and prone to errors. Also, always passing references would mean creating a local variable for each function argument (instead of passing expressions).
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, thenparam_type
is defined asT const
, instead ofT 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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With