6

我正在尝试在 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 有很多重复)。

我尝试了一个依赖于重载的解决方案,但是可以传递的类型之一是我绑定到 std 的任何可调用的类型(这可以是来自 C++03 的常规函子或 C++0x lambda): :function,不幸的是,std::function 的开销虽然非常小,但实际上对这个应用程序产生了影响。

有没有办法解决我目前拥有的问题,或者是否有更好的解决方案一起解决这个问题?

谢谢。

4

2 回答 2

7

正如另一个答案已经描述的那样,您正在尝试访问该类的基类之一中的类成员。这将失败,因为此时该成员尚未声明。

当它实例化基类时,它会实例化它的所有成员声明,​​因此它需要知道返回类型。您可以使返回类型依赖于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()相反,当使用相应的模板参数实例化时,类型需要是完整的。

于 2011-02-21T01:35:19.943 回答
1

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

另一种选择是让包装器从实现类继承而不是相反,这取决于您在更改类层次结构时有多大的灵活性(并记住您有模板类型定义)。例如,您可以编写一个AddParensWrapper<T>继承自Toperator()转发到的a T::call_with_foo。这将解决依赖问题。

于 2011-02-20T23:19:11.220 回答