Remove reference in decltype (return T instead of T& where T& is the decltype)
(If you're a C++11 pro, skip to the bold paragraph.)
Let's say I want to write a template method which calls and returns the result of a passed object which type is the template parameter:
template<ReturnType, T>
ReturnType doSomething(const T & foo) {
return foo.bar(); // EDIT: Might also be an expression introducing a temp val
}
So T
has to have a method ReturnType T::bar() const
in order to be used in a call like this:
struct MyClass {
...
int bar() const;
...
};
...
MyClass object;
int x = doSomething<int, MyClass>(object);
We don't have to write MyClass
thanks to type deduction and the call becomes:
int x = doSomething<int>(object);
But omitting <int>
too results in a compilation error because the method doesn't require to return int in order to be assigned to x
afterwards (it could return char
for example).
In C++0x/11 we have the auto
and decltype
with which we can use to deduce the return type of a template method:
template<T>
auto doSomething(const T & foo) -> decltype(foo.bar()) {
return foo.bar(); // EDIT: Might also be an expression introducing a temp val
}
The compiler will now find out what the type of foo.bar()
is and just uses this as the return type. With our concrete class MyClass
this will be an int
and the following would suffice:
int x = doSomething(object);
Now to my question:
If MyClass defines bar()
as returning an int&
, the return type of doSomething(object)
will also be an int&
= decltype(foo.bar())
. This is a problem, since as G++ now complies that I'm returning reference to temporary.
How can I fix this? Is there something like remove_reference
which can be used like remove_reference(decltype(foo.bar()))
?
I thought about just declaring a helper method which takes a T&
and returns a T
and then define the return type of doSomething
to be decltype(helper(foo.bar()))
. But there has to be a better way, I'm feeling it.
Solution 1:
To remove a reference:
#include <type_traits>
static_assert(std::is_same<int, std::remove_reference<int&>::type>::value, "wat");
In your case:
template <typename T>
auto doSomething(const T& foo)
-> typename std::remove_reference<decltype(foo.bar())>::type
{
return foo.bar();
}
Just to be clear, note that as written returning a reference is just fine:
#include <type_traits>
struct f
{
int& bar() const
{
static int i = 0;
return i;
}
};
template <typename T>
auto doSomething(const T& foo)
-> decltype(foo.bar())
{
return foo.bar();
}
int main()
{
f x;
return doSomething(x);
}
The returned reference can simply be passed on without error. Your example in the comment is where it becomes important and useful:
template <typename T>
auto doSomething(const T& foo)
-> decltype(foo.bar())
{
return foo.bar() + 1; // oops
}