Question

I was experimenting with the newly added rvalue refernces ( in vs2012 express ).

I don't understand something tho. Given the code below ( most of it taken from the c++ standard where std::forward is explained ).

struct A
{
    A(int& i, const float& j):
        m_i(i),
        m_j(j){}

    int& m_i;
    const float& m_j;
};

template<class T, class A1, class A2>
T* factory(A1&& a1, A2&& a2)
{
    return new T(a1, a2);
}

void test()
{

    A* a1 = factory<A>(1, 1.2f);

    //How does this work ?        
    a1->m_i = 2;
}

I don't understand where is m_i binded to.

I will basically have a lvalue reference to an rvalue reference (& &&), that by the ref collapsing rules becomes (&) just a plain lvalue ref. But a reference to what?

Was it helpful?

Solution

I don't understand where is m_i binded to.

m_i is bound to the argument of A's constructor. What is the argument of A's constructor here?

In this case, since factory does not forward its argument to A (i.e. it does not use std::forward<>()), then what is being passed to A is an lvalue. This is because a1 is named, and named objects are lvalues.

The type of a1 is not relevant to determine whether a1 is an lvalue or an rvalue. So even though a1 has type rvalue-reference to int (int&&), as is the case in your program, the parameter a1 is itself a named object, and therefore it is an lvalue.

This means, since m_i has type lvalue-reference to int, that m_i can be bound (and indeed is bound) to factory's (lvalue) parameter a1, which will be destroyed when factory() returns. In other words, you're left with a dangling reference.

Attempting to dereference it (as you do later on in your program) summons Undefined Behavior.

However, if your factory() function had forwarded its arguments to A's constructor:

template<class T, class A1, class A2>
T* factory(A1&& a1, A2&& a2)
{
    return new T(std::forward<A1>(a1), std::forward<A2>(a2));
}

This would have caused a compiler error, because the machinery of std::forward<>() would make sure that lvalues stay lvalues, and rvalues stay rvalues. Attempting to bind an lvalue-reference to an rvalue is illegal, and therefore the call to A's constructor would have failed.

OTHER TIPS

But a reference to what?

This is an undefined behavior. As you have guessed right, it is a reference to a temporary that was destructed in stack of the function factory. To catch such issues in compile time, you need to use std::forward

template<class T, class A1, class A2>
T* factory(A1&& a1, A2&& a2)
{
    return new T(std::forward<A1>(a1), std::forward<A2>(a2));
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top