Search code examples
c++lifetimervalue-referencetype-deductionforwarding-reference

What is the life time of a in "auto&& a= f();" where f() returns an object by value?


#include <iostream>
#include <typeinfo>

class A {};

A f() { return A(); }

int main() {
    auto &&a = f();
    std::cout << typeid(f()).name() << std::endl;
    std::cout << typeid(a).name() << std::endl;
    std::cout << typeid(A{}).name() << std::endl;
    return 0;
}

It outputs

1A
1A
1A

Questions are,

  1. what does 1A mean here? (GCC on a linux box)
  2. auto && should be a forwarding reference, and in this case since f() returns an A object by value, a should be deduced to rvalue reference, correct?
  3. If a is indeed rvalue reference, and since a (temporary) rvalue's lifetime could only be extended by a const lvalue ref, a should go out of scope right after this line, is that correct?

Edit: Fixed the most vexing problem in the last cout statement, as many pointed out.

Summary of the great answers, for posterity:

  1. 1A is internal representation of type A, by Guillaume Racicot. And F1AvE indicates a Function style cast in typeid(A()) resulting in a prvalue, by Ted Lyngmo.
  2. turns out typeid() is not the tool to check reference, as it gives the referenced end type. Ted Lyngmo gave the right way to do it, and yes it is an r-value reference.
static_assert(std::is_rvalue_reference_v<decltype(a)>); // like this, ...
static_assert(std::is_same_v<decltype(a), A&&>);        // or like this
  1. Richard Critten gave the link that says "The lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference (since C++11)...".

Solution

  • what does 1A mean here? (GCC on a linux box)

    It means that the length of the name is 1 followed by the letter. There are many rules on how to mangle a type, but it encodes its namespace, template parameter value and other things.

    auto && should be a forwarding reference, and in this case since f() returns an A object by value, a should be deduced to rvalue reference, correct?

    The typeid operator discards all top level cv and ref qualifiers.

    Also, it see through references and returns the type info for the refered type instead.

    From the typeid operator page:

    If type is a reference type, the result refers to a std::type_info object representing the referenced type.

    But yes, the type deduced is A, so you are declaring A&&. But even if a is an rvalue reference, the expression a is an lvalue.

    If a is indeed rvalue reference, and since a (temporary) rvalue's lifetime could only be extended by a const lvalue ref, a should go out of scope right after this line, is that correct?

    The lifetime end at the end of the scope, per the lifetime extension rules.


    Now for why the typeid is different.

    This is not about rvalue or forwarding references. This is the most vexing parse problem. A() is a function that returns A and has no parameters.

    Use {} for initialization and you'll see the problem disappear:

    #include <iostream>
    #include <typeinfo>
    
    class A {};
    
    A f() { return A(); }
    
    int main() {
        auto &&a = f();
        std::cout << typeid(f()).name() << std::endl; // 1A
        std::cout << typeid(a).name() << std::endl;   // 1A
        std::cout << typeid(A{}).name() << std::endl; // 1A
        return 0;
    }