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

Tim*_*Tim 6 c++ crtp decltype c++11

我试图在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_;
};
Run Code Online (Sandbox Code Playgroud)

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

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

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

谢谢.

Joh*_*itb 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);
    }
};
Run Code Online (Sandbox Code Playgroud)

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