4

I'm testing my understanding of lvalue and rvalue references by intentionally trying to break things. So say there is this struct:

struct FooBar
{
    FooBar(int&& number) : rNumber(number)
    {

    }

    int& rNumber;
};

and I create an instance FooBar obj(5). Every attempt to read the reference variable returns the right result (5). The same happens if I use const int& instead of int&&.

I noticed that replacing int with std::string and reading the reference returns an empty string, so I suspect it gives undefined behaviour. Is this so? And if so, why does it work with integers?

Update: I'm creating the instance and reading it like this:

FooBar obj(5);
//FooBar obj("Hello"); // For strings...

std::cout << obj.rNumber << std::endl;

Update 2: It also works if you pass a user-defined type, like this:

struct GooBar
{
public:
    GooBar(int number) : itsNumber(number) 
    {
        std::cout << "In constructor..." << std::endl;
    }

    GooBar(const GooBar& rhs) = delete;
    GooBar(GooBar&& rhs) = delete;

    ~GooBar() 
    {
        std::cout << "In destructor..." << std::endl;
    }

    int itsNumber;
};


struct FooBar
{
    FooBar(GooBar&& number) : rNumber(number)
    {

    }

    GooBar& rNumber;
};

and then creating an instance and reading it like so:

FooBar obj(GooBar(5));

std::cout << obj.rNumber.itsNumber << std::endl;

I think this is interesting, because it gives the following output:

In constructor...
In destructor...
5
Kristian D'Amato
  • 3,996
  • 9
  • 45
  • 69
  • 1
    This is indeed undefined behavior, do you compile with warnings enabled ? (clang `-Weverything`, for example) – Matthieu M. Dec 14 '13 at 17:46
  • If I enable all warnings I get ~700 warnings, so I cannot point out immediately if any have to do with undefined behaviour. The warnings sourced to my source file don't, at least, but there are others raised in library files. I'm on MSVC btw. – Kristian D'Amato Dec 14 '13 at 17:52
  • Ah, I am sorry for your choice of compiler :( By the way, it does not work for your user-type; you can show it off by setting `itsNumber` to `0` in `GooBar` destructor. – Matthieu M. Dec 14 '13 at 18:07
  • Which compiler are you using @MatthieuM.? On MSVC and GCC 4.7.2 I get the right number displayed after the destructor call! – Kristian D'Amato Dec 14 '13 at 18:32
  • I did. Still works. Shows the changed number on read. – Kristian D'Amato Dec 14 '13 at 18:58
  • Undefined Behavior includes that things can unreasonably work. The memory location where the 5 value is stored, doesn't disappear, so the dangling reference can appear to work. But the location can be *reused*, e.g. by evaluating an expression that involves some function calls, using some stack. – Cheers and hth. - Alf Dec 14 '13 at 18:59
  • The existence of 700 warnings probably means your code base has issues. Start sanitizing. – Yakk - Adam Nevraumont Dec 14 '13 at 20:09
  • @KristianD'Amato: you might want to try Clang if you have access to it, it includes a number of warnings for initializing reference members from temporaries in the constructor; I am not sure whether it already covers the case of r-value references though as those are quite new. – Matthieu M. Dec 15 '13 at 11:03

1 Answers1

2

With an integer literal as actual argument the compiler may pass a reference to a statically allocated instance.

With a std::string formal argument and a string literal as actual argument, the instance is created in the call, and is destroyed at the end of the call.

In both cases it's Undefined Behavior.


It's not clear how you call this though: you forgot to include that crucial information (as the question is as at time I'm writing this).

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • Added that just now. Thanks – Kristian D'Amato Dec 14 '13 at 17:32
  • I think I understand, but I'd appreciate it if you can give me your thoughts on the second update I posted. Thanks! – Kristian D'Amato Dec 14 '13 at 18:11
  • @KristianD'Amato I think it's a dangling reference (Alf can correct me on this.) The object `rNumber` is bound to will exist up until FooBar's constructor ends, then poof, it's gone. As I said, you must call `std::move` on an `lvalue` to get it to work. –  Dec 14 '13 at 18:18
  • I've tried it in CodeBlocks using GCC 4.7.2 and the string version raises an exception when trying to read, but not the integer version. @remyabel I also think it's a dangling reference, but I thought 1. the compiler should raise some sort of warning (it doesn't, neither MSVC nor GCC) and 2. maybe in the case of integers it somehow does the right thing. – Kristian D'Amato Dec 14 '13 at 18:28