c++referencereturn-typeauto

Handling Type Changes with auto in C++


A return type changed from const Bar& getBar() to const Bar getBar().

If i use: const auto& bar = getBar();

and the return type changes. I have a reference on a temporary object.

If i use: const auto bar = getBar();

i always make a copy of Bar.

What is The best practice for this problem?

class Bar {
public:
    Bar() = default;
    Bar(const Bar&) = default;
    Bar(Bar&&) = delete;
    Bar& operator=(const Bar&) = delete;
    Bar& operator=(const Bar&&) = delete;

    void setValue(int i);
    int value() const;

private:
    int m_i = 0;
};

class Foo {
public:
    Foo(Bar& bar) : m_bar(bar) {}
    Foo(const Foo&) = delete;
    Foo(Foo&&) = delete;
    Foo& operator=(const Foo&) = delete;
    Foo& operator=(const Foo&&) = delete;

    const Bar& giveMeBar();

private:
    Bar& m_bar;
};

int main() {

    auto bar = std::make_unique<Bar>();
    auto foo = std::make_unique<Foo>(*bar.get());

    const auto& barFromFoo = foo->giveMeBar();
    bar->setValue(2);

    std::cout << "original bar: " << bar->value() << std::endl;
    std::cout << "bar from foo: " << barFromFoo.value() << std::endl;
}

Solution

  • What is actually "best practice" is opinion based. I will only try to explain why your premise isn't sound so you can make up your opinion on what is a good practice yourself and/or can judge some guideline on the matter.

    If i use: const auto& bar = getBar(); and the return type changes. I have a reference on a temporary object.

    The lifetime of the temporary is extended and bound to the constant reference. There is no problem. If you want to avoid an unnecessary copy then don't make a copy. In the shown code you can replace const Bar& giveMeBar(); with const Bar giveMeBar(); without any issues.

    For illustration consider this example:

    #include <iostream>
    
    
    struct foo {
        ~foo(){ std::cout << "yes. I leave now. Bye\n";}
    };
    
    foo bar() { return foo{};}
    
    int main() {
        const foo& f = bar();
        std::cout << "are you still there?\n";    
    }
    

    Output:

    are you still there?
    yes. I leave now. Bye
    

    PS: auto does not change anything about that. The following are pairwise equivalent:

    const auto& barFromFoo = foo->giveMeBar();   
    const Bar& barFromFoo = foo->giveMeBar();   // same
    
    // and
    const auto barFromFoo = foo->giveMeBar();
    const Bar barFromFoo = foo->giveMeBar();   // same