9

Since const reference is pretty much the same as passing by value but without creating a copy (to my understanding). So is there a case where it is needed to create a copy of the variables (so we would need to use pass by value).

Johnson
  • 145
  • 4
  • 10
  • in C++11 you can use pass by value to "sink" a parameter. I will search and add a link to what that means. – bolov Jan 25 '15 at 15:44
  • http://stackoverflow.com/questions/24543330/when-is-const-reference-better-than-pass-by-value-in-c11 – bolov Jan 25 '15 at 15:46
  • Ah, found it: http://stackoverflow.com/questions/10231349/are-the-days-of-passing-const-stdstring-as-a-parameter-over – bolov Jan 25 '15 at 15:51
  • thanks. i Will read it but it is a bit hard for me :P – Johnson Jan 25 '15 at 16:01
  • See also, with emphasis on the stdlib algorithms taking functors: [Why is templated functor passed as value and not forwarding reference](https://stackoverflow.com/questions/38357159/why-is-templated-functor-passed-as-value-and-not-forwarding-reference) – underscore_d Sep 18 '18 at 19:03

5 Answers5

15

There are situations where you don't modify the input, but you still need an internal copy of the input, and then you may as well take the arguments by value. For example, suppose you have a function that returns a sorted copy of a vector:

template <typename V> V sorted_copy_1(V const & v)
{
    V v_copy = v;
    std::sort(v_copy.begin(), v_copy.end());
    return v;
}

This is fine, but if the user has a vector that they never need for any other purpose, then you have to make a mandatory copy here that may be unnecessary. So just take the argument by value:

template <typename V> V sorted_copy_2(V v)
{
    std::sort(v.begin(), v.end());
    return v;
}

Now the entire process of producing, sorting and returning a vector can be done essentially "in-place".

Less expensive examples are algorithms which consume counters or iterators which need to be modified in the process of the algorithm. Again, taking those by value allows you to use the function parameter directly, rather than requiring a local copy.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
6
  1. It's usually faster to pass basic data types such as ints, floats and pointers by value.
  2. Your function may want to modify the parameter locally, without altering the state of the variable passed in.
  3. C++11 introduces move semantics. To move an object into a function parameter, its type cannot be const reference.
Neil Kirk
  • 21,327
  • 9
  • 53
  • 91
  • Thanks my friend. Also in the 3. answer does that mean that old code will need to be updated due to `const` reference and object not allowed in c++11? – Johnson Jan 25 '15 at 15:51
  • @Johnson No. Most old code should be backwards compatible in C++11. – Neil Kirk Jan 25 '15 at 18:57
5

Like so many things, it's a balance.

We pass by const reference to avoid making a copy of the object.

When you pass a const reference, you pass a pointer (references are pointers with extra sugar to make them taste less bitter). And assuming the object is trivial to copy, of course.

To access a reference, the compiler will have to dereference the pointer to get to the content [assuming it can't be inlined and the compiler optimises away the dereference, but in that case, it will also optimise away the extra copy, so there's no loss from passing by value either].

So, if your copy is "cheaper" than the sum of dereferencing and passing the pointer, then you "win" when you pass by value.

And of course, if you are going to make a copy ANYWAY, then you may just as well make the copy when constructing the argument, rather than copying explicitly later.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
3

The best example is probably the Copy and Swap idiom:

C& operator=(C other)
{
    swap(*this, other);
    return *this;
} 

Taking other by value instead of by const reference makes it much easier to write a correct assignment operator that avoids code duplication and provides a strong exception guarantee!

Also passing iterators and pointers is done by value since it makes those algorithms much more reasonable to code, since they can modify their parameters locally. Otherwise something like std::partition would have to immediately copy its input anyway, which is both inefficient and looks silly. And we all know that avoiding silly-looking code is the number one priority:

template<class BidirIt, class UnaryPredicate>
BidirIt partition(BidirIt first, BidirIt last, UnaryPredicate p)
{
    while (1) {
        while ((first != last) && p(*first)) {
            ++first;
        }
        if (first == last--) break;
        while ((first != last) && !p(*last)) {
            --last;
        }
        if (first == last) break;
        std::iter_swap(first++, last);
    }
    return first;
}
Barry
  • 286,269
  • 29
  • 621
  • 977
1

A const& cannot be changed without a const_cast through the reference, but it can be changed. At any point where code leaves the "analysis range" of your compiler (maybe a function call to a different compilation unit, or through a function pointer it cannot determine the value of at compilation time) it must assume that the value referred to may have changed.

This costs optimization. And it can make it harder to reason about possible bugs or quirks in your code: a reference is non-local state, and functions that operate only on local state and produce no side effects are really easy to reason about. Making your code easy to reason about is a large boon: more time is spent maintaining and fixing code than writing it, and effort spent on performance is fungible (you can spent it where it matters, instead of wasting time on micro optimizations everywhere).

On the other hand, a value requires that the value be copied into local automatic storage, which has costs.

But if your object is cheap to copy, and you don't want the above effect to occur, always take by value as it makes the compilers job of understanding the function easier.

Naturally only when the value is cheap to copy. If expensive to copy, or even if the copy cost is unknown, that cost should be enough to take by const&.

The short version of the above: taking by value makes it easier for you and the compiler to reason about the state of the parameter.

There is another reason. If your object is cheap to move, and you are going to store a local copy anyhow, taking by value opens up efficiencies. If you take a std::string by const&, then make a local copy, one std::string may be created in order to pass thes parameter, and another created for the local copy.

If you took the std::string by value, only one copy will be created (and possibly moved).

For a concrete example:

std::string some_external_state;
void foo( std::string const& str ) {
  some_external_state = str;
}
void bar( std::string str ) {
  some_external_state = std::move(str);
}

then we can compare:

int main() {
  foo("Hello world!");
  bar("Goodbye cruel world.");
}

the call to foo creates a std::string containing "Hello world!". It is then copied again into the some_external_state. 2 copies are made, 1 string discarded.

The call to bar directly creates the std::string parameter. Its state is then moved into some_external_state. 1 copy created, 1 move, 1 (empty) string discarded.

There are also certain exception safety improvements caused by this technique, as any allocation happens outside of bar, while foo could throw a resource exhausted exception.

This only applies when perfect forwarding would be annoying or fail, when moving is known to be cheap, when copying could be expensive, and when you know you are almost certainly going to make a local copy of the parameter.

Finally, there are some small types (like int) which the non-optimized ABI for direct copies is faster than the non-optimized ABI for const& parameters. This mainly matters when coding interfaces that cannot or will not be optimized, and is usually a micro optimization.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thanks, so generally if i have something big to move and i dont want to change it then i will use const reference else if something is "small" then i simply use by vlaue? – Johnson Jan 25 '15 at 15:55
  • @Johnson Things that could be expensive to copy and are probably non-movable should be taken by `const&` almost always. Things that are cheap to copy should be taken by value almost always. Anything in between requires thought. – Yakk - Adam Nevraumont Jan 25 '15 at 15:58
  • Thanks so there could be cases that there might be something cheap to copy but we would be better to use cont reference right but those cases are small right? – Johnson Jan 25 '15 at 16:00
  • @Johnson The ubiquitous rule is to pass class types by const reference, everything else by value. It's not always optimal, but anything that would be optimal would depend on internal knowledge of the type (how expensive is it to copy) or the function being called. This rule is good enough for most circumstances, and easy to follow. – James Kanze Jan 25 '15 at 16:00
  • @ James Kanze thanks and by class types you mean objects right? – Johnson Jan 25 '15 at 16:03
  • @Johnson When writing a function interface taking an instance of a type `Foo` you *usually* know what the `Foo` is and what (roughly) you are doing with it. Are you making a local copy? Is `Foo` cheaply move-able? Then take by value. Is `Foo` a cheap type to copy? Then take by value. Do you actually want changes to the external `Foo` to pass into the parameter in the middle of the function? Take by `const&`. Is `Foo` expensive to copy and has no easy `move`? Take by `const&`. Only a small fraction of concrete parameters will get through the above set of tests. – Yakk - Adam Nevraumont Jan 25 '15 at 16:19
  • @James I'd argue "cheap to copy", "cheap to move", "expensive to copy" and "cannot be moved" are fundamental enough features of a type that you should know them if you are writing a function taking that type as a parameter. – Yakk - Adam Nevraumont Jan 25 '15 at 16:21
  • @Yakk How can you? They are internal details; cheap to copy on one machine may be expensive to copy on another. If you're the client of a class, you shouldn't know; they have nothing to do with the basic semantics of the class. – James Kanze Jan 25 '15 at 16:28
  • @james I disagree, cheap/expensive to copy/move is part of the basic semantics of a class. It changes how you store it, how you pass it as a parameter, how you return it, and how (in practice) it is returned to you. Without that knowledge you are forced to use C++03 style "value semantics are practically unusable" style coding as a defensive measure against your ignorance. Which I guess is your position: never take by value. But, I know that a `std::vector` is cheap to move and expensive to copy on every reasonable platform, so no, it is need not be platform dependent. – Yakk - Adam Nevraumont Jan 25 '15 at 17:06
  • @Yakk The cost of an operation is never part of the semantics. For that matter, whether a type supports move or not isn't normally part of the semantics (although there are exceptions here, when a type supports move but not copy). The cost of an operation is an issue of optimization; you really shouldn't be taking it into account until the profiler says you have to. – James Kanze Jan 26 '15 at 13:04