我偶然发现自上周以来我无法通过的事情......
有这个:
template<typename> struct fx;
template<typename R, typename...Args>
struct fx<R(Args...)>
{
virtual R operator()(const Args & ...x) const = 0;
};
和此:
template<typename> struct fx_err;
// I feel here something is wrong, but I can't figure it out.
template<template<typename> class F, typename R, typename... Args>
struct fx_err< F<R(Args...)> > : fx<R(R,Args...)>
{
using fx_type = F<R(Args...)>;
fx_type f;
R operator ()(const R &y, const Args & ...x) const override
{
return y - f(x...);
}
};
和此:
struct example_fun : fx<int(int,int,int)>
{
int operator() (const int &a, const int &b, const int &c) const override
{
return a * b * c;
}
};
最后我尝试使用它:
fx_err<example_fun> example;
int err = example(24,2,3,4);
编译器抛出错误:'示例具有不完整类型'。
类似的东西只有在我没有专门化fx_err并使用指向fx_type仿函数的指针时才有效,但是我需要添加构造函数来抓取指针本身,这不是我想要的东西。
非常令人沮丧。这有什么问题?我想要达到的目标是否可能?有人可以帮忙吗?
提前致谢。
更新:
这里是为那些愿意尝试这个例子的人提供的示例代码:http://pastebin.com/i3bRF8tB
答案 0 :(得分:2)
问题在线:
fx_err<example_fun> example;
是由example_fun
被传递的事实引起的。到fx_err
,选择声明:
template<typename> struct fx_err;
是一个不完整的类型。
您提供的专业化:
// I feel here something is wrong, but I can't figure it out.
template<template<typename> class F, typename R, typename... Args>
struct fx_err< F<R(Args...)> > : fx<R(R,Args...)>
{ ... }
无法选择,因为example_fun
不是以下所需的模板类:
template<typename> class F
答案 1 :(得分:1)
如果可以,请避免使用模板模板参数。它们比您可能想要的更复杂,灵活性更低。
您似乎正在尝试将fx
基类的形式与其派生类进行匹配。部分特化需要完全匹配,它不会切片到基类。即使它确实如此,这个成员将是抽象类类型:
using fx_type = F<R(Args...)>;
fx_type f; // same as fx<R(Args...)> which is abstract
解决方案是保留派生类,并告诉部分特化如何找到基类。然后部分特化可以在基类上进行模式匹配。
template<typename derived, typename base = typename derived::fx>
struct fx_err;
template<typename derived, template<typename> class F, typename R, typename... Args>
struct fx_err< derived, F<R(Args...)> > : F<R(R,Args...)>
实时解决方案:http://coliru.stacked-crooked.com/a/870172bcad0a9034
当然,通过typename derived::fx
排序查找基类会引发使用什么基类模板的问题。从理论上讲,您可以拥有多个同名模板,或derived
可以拥有成员typedef my_base fx;
,而不是继承fx
专业化。
但更有可能的是,您根本不需要template<typename> class F
。
template<typename derived, typename base = typename derived::fx>
struct fx_err;
template<typename derived, typename R, typename... Args>
struct fx_err< derived, fx<R(Args...)> > : fx<R(R,Args...)>
答案 2 :(得分:0)
fx_err<example_fun>
与您的部分专业化不符,因为example_fun
的格式不属于F<R(Args...)>
。它继承了那种形式,但这不是一回事。
答案 3 :(得分:0)
为类模板选择特化时,不考虑隐式转换。因此,匹配特化时,编译器不会将example_fun
视为fx<...>
,而是选择主要(未定义)模板而不是其他特化。
要解决此问题,您可以在派生类中公开基类的别名:
struct example_fun : fx<int(int,int,int)>
{
using type = fx<int(int,int,int)>;
};
现在在声明网站上使用此别名:
fx_err<example_fun::type> example;
int err = example(24,2,3,4);
您甚至可以使用宏来避免重复基类名称:
template<class T> struct tag { using type = T; };
#define BASE_TAG(B) B, public tag<B>
struct example_fun : BASE_TAG(fx<int(int, int, int)>) {
// ...
};
答案 4 :(得分:0)
你的基本问题是模板类型模式匹配不就像函数重载模板模式匹配一样工作。忽略继承,只传入的类型与模式匹配。
因此,struct foo: some_template<some_args...>
在模板类型模式匹配期间与some_template<some_args...>
不匹配,目的是确定要使用的专业化。
这使我们可以在函数中使用类型作为值:
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
现在,功能模板模式匹配的效果更像您期望的那样:
template<template<class...>class Z, class...Args>
constexpr tag<Z<Args...>> get_template( Z<Args...>const& ) { return {}; }
采用单个参数,模板函数模式匹配和对其进行推导。 将查看传入类型的父级。它会尝试将Z<Args...>
与某个模板Z
匹配。
它返回tag<Z<Args...>>
,这是一种无状态类型,只存储我们需要的类型。然后,我们可以通过别名提供上述内容以提取模板扩展:
template<class T>
using get_template_t = type_t<decltype(get_template(std::declval<T>()))>;
这是大部分的方式。
接下来,我们需要一些SFINAE辅助魔法:
template<class...>struct voider:tag<void>{};
template<class...Ts>using void_t=type_t<voider<Ts...>>;
std::void_t
是C ++ 14,它采用了一堆类型并抛弃它们,而是返回void
。我用2行代码,因为有些编译器在单行版本上失败了。
好的,现在我们攻击fx_err
:
template<class,class=void> struct fx_err;
第二个class=void
让我们做FSINAE工作。我们从您的
template<template<class...>class F, class R, class...Args>
struct fx_err< F<R(Args...)>, void > : fx<R(R,Args...)>
{
using fx_type = F<R(Args...)>;
fx_type f;
R operator ()(const R &y, const Args & ...x) const override {
return y - f(x...);
}
};
我们也这样做:
template<class T>
struct fx_err< T, void_t<get_template_t<T>>> : fx_err<get_template_t<T>>
{};
我觉得应该有用。如果没有,我们只需要添加一个测试,以从此专业化中排除直接T<F(Args...)>
案例。