Использование auto и decltype для возврата ссылки из функции в шаблоном классе

Как я могу принудить функцию в templated классе возвращать ссылку на переменную-член, используя auto/decltype?

Вот тривиализованный пример того, что я пытаюсь сделать. Предположим, у вас есть шаблонный класс, который хранит что-то в частной переменной-члене, a_ следующим образом:

#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;
}

Ожидаемый/желаемый результат:

Value of a = 3
Value of a = 5

Но теперь предположим, что я хочу, чтобы компилятор выводил тип, возвращаемый функциями const и non-const get() в A<T>, и я хочу, чтобы оба вызова возвращали ссылки на a_.

Мое лучшее предположение в настоящее время:

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_;
  }
};

но это не скомпилируется. Первая ошибка, заданная GCC:

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

Мотивация для этого заключается в моем дистиллированном примерном коде, но это связано с попыткой уменьшить количество параметров, которые принимает шаблон, когда один (или более) из этих параметров используется исключительно для указания типа возврата, который должен использовать компилятор ( Я думаю) уметь вывести сам по себе. Примечание: в реальном мире тип возврата get() не относится к a_, а является типом возврата некоторой функции f(a_), который, как я знаю, должен быть выведен компилятором. Таким образом, моя потребность в auto/decltype в этом примере.

То, что меня озадачивает, заключается в том, что компилятор может правильно выводить возвращаемый тип, используя почти идентичный код в не-шаблонном классе:

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_;
  }
};

Любая помощь, чтобы понять, что мне не хватает, будет с благодарностью.

Подробнее:

Centos 6.5
gcc (GCC) 4.7.2 20121015 (Red Hat 4.7.2-5)

Ответ 1

Просто чтобы упомянуть об этом, вам фактически не нужно использовать std::add_lvalue_reference, чтобы получить нужное поведение. Это работает так же хорошо и более читаемо в моей книге.

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;
}

Ответ 2

Оберните a_ в дополнительную пару круглых скобок в возвращаемом типе возврата, чтобы получить тип выражения a_ вместо объявленного типа переменной 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_;
}

или если С++ 1y доступен:

// 1. Return const reference to a_
auto& get() const
{
  return a_;
}

// 2. Return non-const reference to a_
auto& get()
{
  return a_;
}