是否可以在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.
我的直观假设是,如果编译器能够推断f的参数的类型int,那么它也应该用于返回bool,因为这两种类型在模板实例化时都是已知的.
我尝试使用尾随的返回类型函数语法,但是然后找不到一个工作表达式来放入decltype.
编辑1
对于函数具有一个或多个模板参数的情况,DietmarKühl提供了一种基于使用间接层延迟模板实例化的解决方案.不幸的是,当基类函数没有任何参数时,这不起作用,如下所示:
template <typename R> R g() { return static_cast<Derived&>(*this).g_impl(); }
尝试使用相同的技术失败,因为不存在依赖类型.如何处理这种情况?
编辑2
正如Johannes Schaub指出的那样,C 11具有默认的模板参数,因此总是可以使g依赖于任意类型,然后应用Dietmar的解决方案:
template <typename T = void> auto g() -> typename g_impl_result<Derived,T>::type { return static_cast<Derived&>(*this).g_impl(); }
编辑3
C 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; } };
解决方法
一个额外的间接是你的朋友:
template <typename D,typename T> struct f_impl_result { typedef decltype(static_cast<D*>(0)->f_impl(std::declval<T>())) type; }; template <typename Derived> struct base { template <typename T> auto f(T x) -> typename f_impl_result<Derived,T>::type { return static_cast<Derived&>(*this).f_impl(x); } };