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)
In C++14, you can just use auto as a return type.
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 template function whose return type depends on the types of its template arguments.
C++: “auto” return type deduction The “auto” keyword used to say the compiler: “The return type of this function is declared at the end”. In C++14, the compiler deduces the return type of the methods that have “auto” as return type.
'auto' lets you declare a variable with a particular type whereas decltype lets you extract the type from the variable so decltype is sort of an operator that evaluates the type of passed expression.
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.
template <typename T>
class A {
private:
T a_;
public:
A(T a) : a_(a) {}
const auto get() const -> const decltype(a_) & {
return a_;
}
auto get() -> decltype(a_) & {
return a_;
}
};
int main() {
A<int> a(1);
cout << a.get() << endl;
a.get() = 2;
cout << a.get() << endl;
}
Wrap a_
in an extra pair of parentheses in the trailing return type to get the type of the expression a_
instead of the declared type of the variable a_
(Live at Coliru):
// 1. Return const reference to a_
auto get() const -> decltype((a_))
{
return a_;
}
// 2. Return non-const reference to a_
auto get() -> decltype((a_))
{
return a_;
}
or if C++1y is available:
// 1. Return const reference to a_
auto& get() const
{
return a_;
}
// 2. Return non-const reference to a_
auto& get()
{
return a_;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With