Вывод типа шаблона функции

У меня есть некоторый класс C с const и не const getters для некоторого общего типа Node:

template <typename NodeType>
class CParent{};

class Node {};

class C : public CParent<Node> {
    Node&       getNode(Index i);
    const Node& getNode(Index i) const;
};

Теперь я хочу создать функцию псевдонима, которая вызывает getNode для объекта класса C:

template <class CType>
NodeType& AliasGetNode(CType* cobject);

Но как я могу сделать вывод NodeType? то есть, если я называю AliasGetNode<const C>(c) и AliasGetNode<C>(c), NodeType должен быть соответственно const Node& и Node&.

Как я могу это сделать?

Я попробовал подходы result_of и decltype, но не был успешным.

Ответ 1

Я бы порекомендовал:

template <class CType>
auto AliasGetNode(CType& cobject) -> decltype(cobject.getNode(0))
{
    return cobject.getNode(0);
}

Это должно работать с С++ 11

Ответ 2

Пусть компилятор выводит возвращаемый тип (с С++ 14):

template <class CType>
decltype(auto) AliasGetNode(CType& cobject)
{
    return cobject.getNode(0);
}

Ответ 3

Определите простой вспомогательный признак, который добавит/удалит const из типа, основанного на том, есть ли другой тип const:

template <class Src, class Dst>
using copy_const = typename std::conditional<
  std::is_const<Src>::value,
  const Dst,
  typename std::remove_const<Dst>::type
>::type;

И используйте его:

template <class CType>
copy_const<CType, NodeType>& AliasGetNode(CType* cobject);

Ответ 4

Так как С++ 14, компилятор может вывести тип возвращаемой функции:

template<typename CType>
decltype(auto) AliasGetNode(CType& cobject) {
    return cobject.getNode();
}

Когда вы вызываете AliasGetNode объекта типа Node, CType выводится на Node. Но если вы вызываете AliasGetNode на объект типа const Node, CType выводится на const Node.

Важно сделать тип возврата AliasGetNode как decltype(auto), иначе вы потеряете ссылку и константу для возвращаемого типа.

Ответ 5

Вы должны написать две функции псевдонимов: одну для неконстантного экземпляра, а другую для экземпляра const:

template <class CType>
const NodeType& AliasGetNode(const CType* cobject) const;  // for const instance. 
// The function must be const since the only difference between return type will cause redefinition error.

template <class CType>
NodeType& AliasGetNode(CType* cobject);  // for non-const instance

Инстанции, объявленные const, будут вызывать функции const, если есть функции перегрузки. Конечно, неконстантные экземпляры вызовут неконстантную версию функции перегрузки. Например:

class Aclass {
    public:
       string test() { return "calling non-const function"; }
       string test() const { return "calling const function"; }
};

int main() {
    Aclass a;
    const Aclass b;

    cout << a.test() << endl;
    cout << b.test() << endl;

    return 0;
}

Результат будет:

calling non-const function
calling const function