0

Here is the article I am using as a reference, which was ultimately mentioned in this SO answer.

The author gives two examples:

Example 1:

std::vector<std::string> 
sorted(std::vector<std::string> names)
{
    std::sort(names);
    return names;
}

// names is an lvalue; a copy is required so we don't modify names
std::vector<std::string> sorted_names1 = sorted( names );

// get_names() is an rvalue expression; we can omit the copy!
std::vector<std::string> sorted_names2 = sorted( get_names() );

Example 2:

std::vector<std::string> 
sorted2(std::vector<std::string> const& names) // names passed by reference
{
    std::vector<std::string> r(names);        // and explicitly copied
    std::sort(r);
    return r;
}

Then saying:

Although sorted and sorted2 seem at first to be identical, there could be a huge performance difference if a compiler does copy elision. Even if the actual argument to sorted2 is an rvalue, the source of the copy, names, is an lvalue, so the copy can’t be optimized away. In a sense, copy elision is a victim of the separate compilation model: inside the body of sorted2, there’s no information about whether the actual argument to the function is an rvalue; outside, at the call site, there’s no indication that a copy of the argument will eventually be made.

My question is simple: Why cannot the compiler use copy elision in the second example, but it can in the first?

What about passing by value and reference differentiates them? names is named in both instances, so I would assume we also create an lvalue in both instances.

herophant
  • 642
  • 7
  • 16

1 Answers1

0

The difference is that names modifiable object or not. const lvalue reference is not modifiable.


since get_names() is a rvalue, we can use move semantics.

Not always. Consider when the move constructor is called.

However if the parameter is a const lvalue reference, then we must "preserve" the value as an lvalue, so a simple switch of pointers cannot occur

When the parameter is lvalue reference, you can modify the object. However, you cannot pass an rvalue.

std::vector<std::string> 
sorted3(std::vector<std::string>& names)
{
    std::sort(names);
    return names;
}

// names is an lvalue; lvalue reference can bind lvalue. argument will be modified after execution `sorted3`.
std::vector<std::string> sorted_names1 = sorted3( names );

// get_names() is an rvalue expression; we cannot bind by lvalue reference.
//std::vector<std::string> sorted_names2 = sorted3( get_names() );

const lvalue reference can bind rvalue. However, you cannot modify the object so that a simple switch of pointers cannot occur.

yumetodo
  • 1,147
  • 7
  • 19
  • So if we consider `vector names = sorted( get_names () )`, since `get_names()` is a rvalue, we can use move semantics. However if the parameter is a `const` lvalue reference, then we must "preserve" the rvalue as an lvalue, so a simple switch of pointers cannot occur, correct? But when we pass-by-value, then no preservation is needed, correct? Sorry, I'm pretty new to this so some detail would make things clearer. – herophant Oct 13 '19 at 04:32