mav*_*vam 11 c++ templates crtp c++11 c++14
是否可以推断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;
}
Run Code Online (Sandbox Code Playgroud)
这会产生以下错误:
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.
Run Code Online (Sandbox Code Playgroud)
我的直观假设是,如果编译器能够推断int
参数的类型f
,它也应该适用于返回bool
,因为这两种类型在模板实例化时都是已知的.
我尝试使用尾随返回类型函数语法,但后来找不到要放入的工作表达式decltype
.
对于函数具有一个或多个模板化参数的情况,DietmarKühl提供了一种基于使用间接层延迟模板实例化的解决方案.不幸的是,当基类函数没有任何参数时,这不起作用,如下所示:
template <typename R>
R g()
{
return static_cast<Derived&>(*this).g_impl();
}
Run Code Online (Sandbox Code Playgroud)
尝试使用相同的技术失败,因为不存在依赖类型.如何处理这种情况?
正如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();
}
Run Code Online (Sandbox Code Playgroud)
这个问题在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;
}
};
Run Code Online (Sandbox Code Playgroud)
额外的间接是你的朋友:
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);
}
};
Run Code Online (Sandbox Code Playgroud)