c ++ 11 std :: function like functors incomplete type

时间:2015-07-27 00:27:50

标签: c++11

我偶然发现自上周以来我无法通过的事情......

有这个:

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

5 个答案:

答案 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...)>案例。