CTRP 派生 class 中没有名为 'type' 的类型
No type named 'type' in CTRP derived class
我一直在试验 Curiously Recurring Template Pattern 的通用单参数仿函数,并有两种实现方式:一种是使用有效的模板模板参数,另一种是我尝试访问派生的 Functor::type 在界面中 class。在后一个示例中,编译器 (gcc 5.4.0) 报告
error: no type named 'type' in 'struct Cube< double >'
template<class T, template<class> class Functor>
class FunctorInterface_1 {
private:
const Functor<T> &f_cref;
public:
FunctorInterface_1() : f_cref(static_cast<const Functor<T>&>(*this)) {}
T operator() ( T val ) const { return f_cref(val); }
}; // FunctorInterface_1 (works)
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
using Ftype = typename Functor::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
然后我尝试在以下两个 classes 的 main() 中使用 T=double 进行编译:
template<class T>
struct Square : public FunctorInterface_1<T,Square> {
T operator()( T val ) const { return val*val; }
}; // Square
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
能否修改 FunctorInterface_2/Cube 示例,或者
接口 class 是否有必要在 T 上模板化为
在第一个例子中?谢谢!
编辑:使用 gcc -std=c++14,我可以编译第二个示例并 运行
通过在 FunctorInterface_1::operator() 中使用自动 return 和参数类型,但是,据我所知,自动参数类型不是 C++14 标准的一部分。
编辑 2:嗯,我觉得有点厚。我刚刚意识到我可以在新参数上模板 FunctorInterface_1::operator(),但是,对于我想到的应用程序,我真的希望我的基础 class 能够访问派生中定义的类型class。
您的代码可以简化为
template<typename TDerived> class
Base
{
using Ftype = typename TDerived::type;
};
template<typename T> class
Derived: public Base<Derived<T>>
{
using type = T;
};
Derived<int> wat;
它不起作用,因为在 Base
实例化时 Derived
class 尚未完成,编译器还不知道 Derived::type
存在。
当行
using Ftype = typename Functor::type;
在基class中处理,Functor
的定义不可用。因此,您不能使用 Functor::type
.
绕过此限制的一种方法是定义特征 class。
// Declare a traits class.
template <typename T> struct FunctorTraits;
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
// Use the traits class to define Ftype
using Ftype = typename FunctorTraits<Functor>::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
// Forward declare Cube to specialize FunctorTraits
template<class T> struct Cube;
// Specialize FunctorTraits for Cube
template <typename T> struct FunctorTraits<Cube<T>>
{
using type = T;
};
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
你必须明白当你实例化时 Cube<T>
FunctionInterface_2<Cube<T>>
首先被实例化。这意味着 Cube<T>
在发生这种情况时是不完整的类型。
因此,当编译器到达使用 Ftype = typename Functor::type;
的行时,Functor
是不完整的,您无法访问它的任何嵌套类型。
在您的情况下,您可以将 FunctionInterface_2
更改为:
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
template <class TT>
auto operator() ( TT && val ) -> decltype(f_cref(val)) const { return f_cref(val); }
};
因此,现在访问有关 Functor
的信息会延迟到您从 FunctionInterface_2
调用 operator()
时,此时 FunctionInterface_2
和 Cube
已完全实例化。
注意: 已经回答了这个问题,但我想详细说明一下并专门解决 clang 的输出问题。
可以在更小的代码示例中演示该问题:(@vtt 类似的东西)
template <typename _CRTP>
struct A {
using _C = typename _CRTP::C;
};
struct B : public A<B> {
using C = int;
};
用 clang 编译它会导致完全误导性的错误消息:(godbolt)
<source>:3:32: error: no type named 'C' in 'B'
using _C = typename _CRTP::C;
~~~~~~~~~~~~~~~~^
<source>:6:19: note: in instantiation of template class 'A<B>' requested here
struct B : public A<B> {
^
1 error generated.
GCC 的错误消息更有帮助:(godbolt)
<source>: In instantiation of 'struct A<B>':
<source>:6:19: required from here
<source>:3:33: error: invalid use of incomplete type 'struct B'
3 | using _C = typename _CRTP::C;
| ^
<source>:6:8: note: forward declaration of 'struct B'
6 | struct B : public A<B> {
| ^
正如接受的答案中所建议的那样,实施特征类型可以解决问题:
// this declaration must appear before the definition of A
template <typename _A>
struct a_traits;
template <typename _CRTP>
struct A {
// `a_traits<_CRTP>::type` is an incomplete type at this point,
// but that doesn't matter since `A` is also incomplete
using _C = typename a_traits<_CRTP>::type;
};
// this specialization must appear before the definition of B
template <>
struct a_traits<struct B> { // adding the type specifier `struct` will declare B
using type = int;
};
// specifying the template parameter will complete the type `A<B>`, which works since
// `a_traits<B>` is already complete at this point
struct B : public A<B> {
using C = int;
};
我一直在试验 Curiously Recurring Template Pattern 的通用单参数仿函数,并有两种实现方式:一种是使用有效的模板模板参数,另一种是我尝试访问派生的 Functor::type 在界面中 class。在后一个示例中,编译器 (gcc 5.4.0) 报告
error: no type named 'type' in 'struct Cube< double >'
template<class T, template<class> class Functor>
class FunctorInterface_1 {
private:
const Functor<T> &f_cref;
public:
FunctorInterface_1() : f_cref(static_cast<const Functor<T>&>(*this)) {}
T operator() ( T val ) const { return f_cref(val); }
}; // FunctorInterface_1 (works)
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
using Ftype = typename Functor::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
然后我尝试在以下两个 classes 的 main() 中使用 T=double 进行编译:
template<class T>
struct Square : public FunctorInterface_1<T,Square> {
T operator()( T val ) const { return val*val; }
}; // Square
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
能否修改 FunctorInterface_2/Cube 示例,或者 接口 class 是否有必要在 T 上模板化为 在第一个例子中?谢谢!
编辑:使用 gcc -std=c++14,我可以编译第二个示例并 运行 通过在 FunctorInterface_1::operator() 中使用自动 return 和参数类型,但是,据我所知,自动参数类型不是 C++14 标准的一部分。
编辑 2:嗯,我觉得有点厚。我刚刚意识到我可以在新参数上模板 FunctorInterface_1::operator(),但是,对于我想到的应用程序,我真的希望我的基础 class 能够访问派生中定义的类型class。
您的代码可以简化为
template<typename TDerived> class
Base
{
using Ftype = typename TDerived::type;
};
template<typename T> class
Derived: public Base<Derived<T>>
{
using type = T;
};
Derived<int> wat;
它不起作用,因为在 Base
实例化时 Derived
class 尚未完成,编译器还不知道 Derived::type
存在。
当行
using Ftype = typename Functor::type;
在基class中处理,Functor
的定义不可用。因此,您不能使用 Functor::type
.
绕过此限制的一种方法是定义特征 class。
// Declare a traits class.
template <typename T> struct FunctorTraits;
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
// Use the traits class to define Ftype
using Ftype = typename FunctorTraits<Functor>::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
// Forward declare Cube to specialize FunctorTraits
template<class T> struct Cube;
// Specialize FunctorTraits for Cube
template <typename T> struct FunctorTraits<Cube<T>>
{
using type = T;
};
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
你必须明白当你实例化时 Cube<T>
FunctionInterface_2<Cube<T>>
首先被实例化。这意味着 Cube<T>
在发生这种情况时是不完整的类型。
因此,当编译器到达使用 Ftype = typename Functor::type;
的行时,Functor
是不完整的,您无法访问它的任何嵌套类型。
在您的情况下,您可以将 FunctionInterface_2
更改为:
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
template <class TT>
auto operator() ( TT && val ) -> decltype(f_cref(val)) const { return f_cref(val); }
};
因此,现在访问有关 Functor
的信息会延迟到您从 FunctionInterface_2
调用 operator()
时,此时 FunctionInterface_2
和 Cube
已完全实例化。
注意:
可以在更小的代码示例中演示该问题:(@vtt
template <typename _CRTP>
struct A {
using _C = typename _CRTP::C;
};
struct B : public A<B> {
using C = int;
};
用 clang 编译它会导致完全误导性的错误消息:(godbolt)
<source>:3:32: error: no type named 'C' in 'B'
using _C = typename _CRTP::C;
~~~~~~~~~~~~~~~~^
<source>:6:19: note: in instantiation of template class 'A<B>' requested here
struct B : public A<B> {
^
1 error generated.
GCC 的错误消息更有帮助:(godbolt)
<source>: In instantiation of 'struct A<B>':
<source>:6:19: required from here
<source>:3:33: error: invalid use of incomplete type 'struct B'
3 | using _C = typename _CRTP::C;
| ^
<source>:6:8: note: forward declaration of 'struct B'
6 | struct B : public A<B> {
| ^
正如接受的答案中所建议的那样,实施特征类型可以解决问题:
// this declaration must appear before the definition of A
template <typename _A>
struct a_traits;
template <typename _CRTP>
struct A {
// `a_traits<_CRTP>::type` is an incomplete type at this point,
// but that doesn't matter since `A` is also incomplete
using _C = typename a_traits<_CRTP>::type;
};
// this specialization must appear before the definition of B
template <>
struct a_traits<struct B> { // adding the type specifier `struct` will declare B
using type = int;
};
// specifying the template parameter will complete the type `A<B>`, which works since
// `a_traits<B>` is already complete at this point
struct B : public A<B> {
using C = int;
};