Using auto and decltype to return reference from member function in a class template

auto, c++, c++11, decltype, templates

Solution

Just to mention it, you don't actually have to use `std::add_lvalue_reference` to get the behaviour you want. This works just as well and is more readable in my book.

#include <iostream>

template <typename T>
class A {
private:
    T a_;

public:
    A(T a) : a_(a) {}

    auto get() const -> const decltype(a_) & {
        return a_;
    }

    auto get() -> decltype(a_) & {
        return a_;
    }
};

int main() {
    A<int> a(1);
    std::cout << a.get() << std::endl;
    a.get() = 2;
    std::cout << a.get() << std::endl;
}
1
2

Problem

How can I coerce a function in a templated class to return a reference to a member variable using auto/decltype? Here's a trivialized example of what I'm trying to do. Suppose you've got a templated class that stores something in a private member variable, `a_` as follows: ``` #include <iostream> template <typename T> class A { private: T a_; public: A(T a) : a_(a) {} // 1. Return const reference to a_ const T & get() const { return a_; } // 2. Return non-const reference to a_ T & get() { return a_; } }; int main(int argc, char *argv[]) { A<int> a(3); const auto & a1 = a.get(); // 1. Return const reference to a_ //a1 = 4; // Shouldn't compile std::cout << "Value of a = " << a.get() << std::endl; auto & a2 = a.get(); // 2. Return non-const reference to a_ a2 = 5; std::cout << "Value of a = " << a.get() << std::endl; return 0; } ``` The expected/desired output is: ``` Value of a = 3 Value of a = 5 ``` But now, suppose I want the compiler to deduce the type returned by the const and non-const `get()` functions in `A<T>` and I want to ensure both calls return references to `a_`. My best guess is currently: ``` template <typename T> class A { private: T a_; public: A(T a) : a_(a) {} // 1. Return const reference to a_ const auto get() const -> std::add_lvalue_reference<const decltype(a_)>::type { return a_; } // 2. Return non-const reference to a_ auto get() -> std::add_lvalue_reference<decltype(a_)>::type { return a_; } }; ``` but that fails to compile. The first error given by GCC is: ``` decltype.cpp:11:29: error: expected type-specifier decltype.cpp:11:26: error: expected ‘;’ at end of member declaration decltype.cpp:11:29: error: ‘add_lvalue_reference’ in namespace ‘std’ does not name a type ``` The motivation for this lies outwith my distilled example code, but stems from an attempt to reduce the number of parameters a template takes when one (or more) of those parameters is used solely to specify a return type which the compiler should (I think) be able to deduce by itself. Note: in the real world, the return type of `get()` is not that of `a_`, but is the return type of some function `f(a_)` which I know to be deducible by the compiler. Thus my need for auto/decltype in this example. The thing that's puzzling me is that the compiler can deduce the return type correctly using near-identical code in a non-templated class: ``` class A { private: int a_; public: A(int a) : a_(a) {} // 1. Return const reference to a_ const auto get() const -> std::add_lvalue_reference<const decltype(a_)>::type { return a_; } // 2. Return non-const reference to a_ auto get() -> std::add_lvalue_reference<decltype(a_)>::type { return a_; } }; ``` Any help to understand what I'm missing will be greatly appreciated. Details: ``` Centos 6.5 gcc (GCC) 4.7.2 20121015 (Red Hat 4.7.2-5) ```

Original source

Related problems