c++c++11autoreturn-type-deduction

auto return type not deducing reference


I have the following code:

#include <iostream>

struct C {
    int a;
    int& get() { return a; }
};
struct D {
    int a;
    int get() { return a; }
};
template <typename T>
auto foo(T o) { // no sample code is complete without a foo
    return o.get();
}

int main()
{
    C c;
    D d;
    c.a = 2;
    d.a = 42; // no sample code is complete without a 42
    std::cout << "c=" << c.a << ", d=" << d.a << "\n";
    c.get() = 3;
    //d.get() = 43;
    std::cout << "c=" << c.a << ", d=" << d.a << "\n";
    foo(c) = 4;  // <--- why won't this work?
    //foo(d) = 44;
    std::cout << "c=" << c.a << ", d=" << d.a << "\n";
}

Why won't the line marked compile? It seems that the copiler is deducing the return type of foo<C> to be int instead of the int& I would expect. Why is this and how can I get it to deduce the reference, too?

http://coliru.stacked-crooked.com/a/6ab909680836fd24


Solution

  • Given auto, which is declared as a non-reference, so we're dealing with pass-by-value case. And auto follows the rules of template argument deduction; the reference part of int& will be ignored, then the deduced type is int.

    You can use decltype(auto) (since C++14) instead.

    type is decltype(e), where e is the initializer.

    template <typename T>
    decltype(auto) foo(T&& o) { // no sample code is complete without a foo
        return o.get();
    }
    

    The return type is deduced as decltype(o.get()), and according to the rule of decltype,

    if the value category of expression is lvalue, then decltype yields T&;

    c.get() returns int&, which is an lvalue, then we get the return type int& insteand of int.

    BTW: Note that if o is still passed by-value, the returned reference would be dangled.