简体   繁体   中英

C++ return by reference and return by const-reference value is copied

I have some questions about returing a reference of a class member variable.

I have the following code:

#include <stdint.h>
#include <string>
#include <iostream>
#include <set>

void
PrintSet (const std::string & str, const std::set<uint32_t> & to_print)
{
  std::cout << str << " (" << to_print.size () << "): ";

  for (std::set<uint32_t>::const_iterator it = to_print.begin ();
          it != to_print.end (); ++it)
    {
      std::cout << *it << " ";
    }

  std::cout << "\n";
}

class Test
{
private:
  std::set<uint32_t> m_values;

public:

  Test () : m_values () { }

  void
  SetValues (const std::set<uint32_t> & values)
  {
    m_values = values;
  }

  const std::set<uint32_t> &
  GetValues () const
  {
    return m_values;
  }

  std::set<uint32_t> &
  GetValues ()
  {
    return m_values;
  }

  void
  Print () const
  {
    PrintSet ("TestInst", m_values);
  }
};
  1. I noticed that if I do this:

     std::set<uint32_t> returned = test.GetValues (); 

    The variable returned gets a copy , not a reference, why?

  2. Does the const std::set<uint32_t> & GetValues () const function must contain the double const (the one on the return value and the one after the function name) ?

EDIT: Additional questions:

  1. Knowing that std::set<uint32_t> returned = test.GetValues (); creates a copy. If I do this:

     test.GetValues ().size (); 

    In order to call the size () function (or any other member of the returned object), is a copy created temporarily or all is resolved with the returned reference?

  2. Is it bad to have three functions, return by value, by const-reference and by reference?

     // Return by value std::set<uint32_t> GetValues () const { return m_values; } // Return by const-reference const std::set<uint32_t> & GetValues () const { return m_values; } // Return by reference std::set<uint32_t> & GetValues () { return m_values; } 

The variable returned gets a copy, not a reference, why?

When you use a reference to initialize a value whose type is not a reference you get a copy, for example

#include <cassert>

int main() {
    int a = 1;
    const int& a_ref = a;
    int b = a_ref;
    assert(&b != &a);
}

Does the const std::set<uint32_t> & GetValues () const function must contain the double const

The second const is applied as a member function qualifier, it means that the method can be called when the calling instance of your class is const qualified. For example

Test test;
test.GetValues(); // 1
const Test& test_ref = test;
test_ref.GetValues(); // 2

Here 1 will call the non-const version and 2 will call the method qualified with const

Further a const qualified method will not let you return non-const references to it's own values, so you must return a const reference to your member variable m_values .

So if you include the second const then you must include the first const , however if you just make the return type be a const reference then you don't need to make the method const . For example

const std::set<uint32_t>& GetValues() // 1
std::set<uint32_t>& GetValues() { return m_values; } // 2

here 1 is allowed but 2 is not allowed.

The reason this happens if you are curious, is because the implicit this pointer is const qualified to be a pointer to const in const qualified methods.


In order to call the size () function (or any other member of the returned object), is a copy created temporarily or all is resolved with the returned reference?

The size() method will be called on the reference! The best way to test things like this is to try it out in a quick test case https://wandbox.org/permlink/KGSOXDkQESc8ENPW (note that I have made the test a little more complicated than needed just for demonstration)

Is it bad to have three functions, return by value, by const-reference and by reference?

If you are exposing the user to a mutable reference, then the best way to let them make a copy (which is why you had the by-value) method is to let them make a copy themselves by initializing a non-ref qualified set with the returned reference (like you did initially)

Also in your code there is am ambiguity between the following two methods

std::set<uint32_t> GetValues() const
const std::set<uint32_t>& GetValues () const

Because all that is different is the return type, and you cannot overload a function off the return type .

The variable returned is a separate object has to have its own value.

Thus in this statement

std::set<uint32_t> returned = test.GetValues ();

the value referenced by the reference returned by the function is copied into the object returned .

If instead of the object you declare a reference as for example

std::set<uint32_t> &returned = test.GetValues ();

then in this case the reference returned will reference the original object of the class object and is initialized by the reference returned by the function.

As for the second question then the returned type does not take part in the overloaded function resolution.

So for example if you will drop the second const qualifier

const std::set<uint32_t> & GetValues ();
std::set<uint32_t> & GetValues (); 

then there is an ambiguity between these two function for a call like this

std::set<uint32_t> returned = test.GetValues ();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM