我正在尝试在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的开销虽然非常小,但实际上在这个应用程序中有所不同。
有没有办法解决我目前的问题,还是有更好的解决方案来解决这个问题?
感谢。
答案 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()
。这将解决依赖性问题。