Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 431
leemes Avatar asked Nov 02 '12 20:11

leemes


People also ask

What does decltype return?

The decltype type specifier yields the type of a specified expression. The decltype type specifier, together with the auto keyword, is useful primarily to developers who write template libraries. Use auto and decltype to declare a function template whose return type depends on the types of its template arguments.

What does decltype in c++?

In the C++ programming language, decltype is a keyword used to query the type of an expression. Introduced in C++11, its primary intended use is in generic programming, where it is often difficult, or even impossible, to express types that depend on template parameters.

Why to use decltype?

You should use decltype when you want a new variable with precisely the same type as the original variable. You should use auto when you want to assign the value of some expression to a new variable and you want or need its type to be deduced.


1 Answers

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 } 
like image 168
GManNickG Avatar answered Oct 03 '22 08:10

GManNickG