在CRTP基类的后期指定返回中使用decltype

时间:2011-02-20 23:13:45

标签: c++ c++11 crtp decltype

我正在尝试在CRTP基类中的成员函数的后期指定返回中使用decltype,并且它出错:invalid use of incomplete type const struct AnyOp<main()::<lambda(int)> >

template<class Op>
struct Operation
{
    template<class Foo>
    auto operator()(const Foo &foo) const ->
        typename std::enable_if<is_foo<Foo>::value,
                                decltype(static_cast<const Op*>(nullptr)->call_with_foo(foo))>::type     
    {
        return static_cast<const Op*>(this)->call_with_foo(foo);
    }
};


template<class Functor>
struct AnyOp : Operation<AnyOp<Functor> >
{
    explicit AnyOp(Functor func) : func_(func) {}

    template<class Foo>
    bool call_with_foo(const Foo &foo) const
    {
        //do whatever
    }

  private:
    Functor func_;
};

我基本上试图将所有sfinae锅炉板移动到基类中,所以我不需要为我创建的每个操作重复它(目前每个操作有6个不同的调用,并且有大约50个操作所以使用enable_if进行了相当多的重复。

我尝试过依赖重载的解决方案,但可以传递的类型之一是可调用的任何东西(这可以是来自C ++ 03或C ++ 0x lambda的常规仿函数),我绑定了不幸的是,std :: function来自std :: function的开销虽然非常小,但实际上在这个应用程序中有所不同。

有没有办法解决我目前的问题,还是有更好的解决方案来解决这个问题?

感谢。

2 个答案:

答案 0 :(得分:6)

正如另一个答案所描述的那样,您正试图访问其中一个类的基类中的某个类的成员。这将失败,因为该成员在那时尚未宣布。

当它实例化基类时,它会实例化其所有成员声明,​​因此需要知道返回类型。您可以使返回类型依赖于Foo,这会使延迟计算返回类型,直到Foo已知。这将改变基类,如下所示

// ignore<T, U> == identity<T>
template<typename T, typename Ignore> 
struct ignore { typedef T type; };

template<class Op>
struct Operation
{
    template<class Foo>
    auto operator()(const Foo &foo) const ->
        typename std::enable_if<is_foo<Foo>::value,
           decltype(static_cast<typename ignore<const Op*, Foo>::type>(nullptr)->call_with_foo(foo))>::type     
    {
        return static_cast<const Op*>(this)->call_with_foo(foo);
    }
};

这会人为地将static_cast强制转换为依赖于Foo的类型,因此它不会立即需要完整的Op类型。相反,当使用相应的模板参数实例化operator()时,类型需要完整。

答案 1 :(得分:1)

您正在尝试从其自己的基类之一引用类的成员,这将失败,因为类的主体在其基类中不存在。你可以将用于计算返回类型call_with_foo作为元函数的逻辑传递给基类吗?这种逻辑会变得复杂吗?

另一个选择,取决于你在更改类层次结构方面的灵活性(并记住你有模板typedef),是让包装器继承实现类而不是相反。例如,您可以编写一个继承自AddParensWrapper<T>T的{​​{1}}转发到operator()。这将解决依赖性问题。