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?
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)
, wheree
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.