Можно ли вывести возвращаемый тип шаблонной функции-члена в базовый класс CRTP?
В то время как вывод типов аргументов хорошо работает, он терпит неудачу с возвращаемым типом. Рассмотрим пример ниже.
#include <iostream>
template <typename Derived>
struct base
{
template <typename R, typename T>
R f(T x)
{
return static_cast<Derived&>(*this).f_impl(x);
}
};
struct derived : base<derived>
{
bool f_impl(int x)
{
std::cout << "f(" << x << ")" << std::endl;
return true;
}
};
int main()
{
bool b = derived{}.f(42);
return b ? 0 : 1;
}
Это приводит к следующей ошибке:
bool b = derived{}.f(42);
~~~~~~~~~~^
crtp.cc:7:5: note: candidate template ignored: couldn't infer template argument 'R'
R f(T x)
^
1 error generated.
Мое интуитивное предположение заключается в том, что если компилятор способен вывести тип int
для аргумента f
, он также должен работать для return bool
, поскольку оба типа известны во время создания шаблона.
Я попытался использовать синтаксис функции возвращаемого типа возвращаемого типа, но затем не смог найти рабочее выражение для ввода decltype
.
РЕДАКТИРОВАТЬ 1
В случае, когда функция имеет один или несколько шаблонных аргументов, Dietmar Kühl предоставил решение, основанное на отсрочке создания экземпляра шаблона, используя слой косвенности. К сожалению, это не работает, когда функция базового класса не имеет никаких аргументов, например:
template <typename R>
R g()
{
return static_cast<Derived&>(*this).g_impl();
}
Попытки использовать один и тот же метод не работают, потому что не существует зависимых типов. Как обрабатывать этот случай?
EDIT 2
Как указано Johannes Schaub, С++ 11 содержит аргументы шаблона по умолчанию, поэтому всегда можно сделать g
зависимым от произвольного типа, а затем применить решение Dietmar:
template <typename T = void>
auto g() -> typename g_impl_result<Derived, T>::type
{
return static_cast<Derived&>(*this).g_impl();
}
РЕДАКТИРОВАТЬ 3
Эта проблема больше не существует в С++ 14, так как у нас есть возвращаемый тип вывода для нормальных функций, что позволяет нам просто написать:
template <typename Derived>
struct base
{
template <typename T>
auto f(T x)
{
return static_cast<Derived&>(*this).f_impl(x);
}
auto g()
{
return static_cast<Derived&>(*this).g_impl();
}
};
struct derived : base<derived>
{
bool f_impl(int x)
{
return true;
}
double g_impl()
{
return 4.2;
}
};