2

Given the new C++11 standard and the move semantics introduced in it, could it be possible to create default values for constructor references? Here Default value to a parameter while passing by reference in C++ it is said that no, but perhaps the new standard allows some trickiness.

Basically what I want is to use a default object for the normal usage and to pass a mock object that records all the calls the host object has done in the testing phase. It should be something like

class A {
   B& b;
   public:
       A(B & b = B()){} // This does not work
}

and when testing what I want is

BMock bMock;
A a(bMock);
bMock.getStatistics();

Any ideas?

Community
  • 1
  • 1
tonicebrian
  • 4,715
  • 5
  • 41
  • 65

3 Answers3

4

How about this:

class Foo
{
  B default_B;

public:

  std::reference_wrapper<B> b;

  Foo() : b(std::ref(default_B)) { }

  Foo(B & br) b(std::ref(br)) { }
};

Here's an alternative idea that might be lighter-weight, at the expense of dynamic allocation in the default case:

#include <memory>

class Foo
{
  std::unique_ptr<B> pB;

public:

  std::reference_wrapper<B> b;

  Foo() : pb(new B), b(std::ref(*pb)) { }

  Foo(B & br) pb(), b(std::ref(br)) { }
};
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • +1… this answers the question he asked, not the one I originally assumed he was asking. The default object should possibly be `static`, though. And I don't know if the `reference_wrapper` is really better than a pointer here. – Potatoswatter Nov 29 '11 at 06:13
  • I think this solution is closer and I think it solves perfectly my problem. But just out of curiosity, how would you solve the problem that when I'm using the second constructor, the object default_B es wasted on each Foo instance? And the static solution won't be applicable as was pointed by UncleBen – tonicebrian Nov 29 '11 at 10:42
  • @ancechu: If you insist on having one default object *per instance*, then you'll somehow have to live with the fact that there's a default object somewhere. Maybe it can be pimpled out, let me edit. – Kerrek SB Nov 30 '11 at 16:34
1

Yes, the default initializer expression is a temporary object, and it will bind to an rvalue reference. However, you need to pass non-temporary objects through std::move to get them to bind to the reference.

Usually passing something through std::move indicates that it will be destroyed by the called function. The standard goes so far as to call the result of std::move an "expiring value;" although there's no real semantic necessity for it to expire, it's a pretty strong convention.

So, you can do this, but be careful.

Looking closer at your example, you want to keep a reference to b inside a, which means that there's nowhere to permanently store the proposed default value if no argument were used. You would be retaining a reference to a destroyed temporary. So unfortunately I can't really adapt your example…

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
0

Just an idea, what about this:

class A 
{
    std::shared_ptr<B> m_b;

public:
    A() : m_b(new B) {}
    A(B& b) : m_b(&b, [](B*){}) {}
};

On another note, I'm against storing off anything you take as a reference, I don't feel it connotes your usage at all. I'm also against members which are references, as they disallow assignment operators.

David
  • 27,652
  • 18
  • 89
  • 138