我想了解是否可以在基本CRTP类中使用叶CRTP类的嵌套类。下面的例子演示了这个问题。
#include <iostream>
using namespace std;
template<class T>
class A
{
protected:
T* asLeaf(void)
{return static_cast<T*>(this);}
T const* asLeaf(void) const
{return static_cast<T const*>(this);}
public:
struct Inner
{int a = 10;};
void dispInner(void) const
{std::cout << asLeaf()->inner.a << std::endl;}
// I would like to use T::Inner in this class, e.g.
// typename T::Inner mvInnerA;
// However, I understand that it is not possible to
// use it in the form that is stated above. Thus,
// I am looking for any possible workarounds.
};
class B: public A<B>
{
public:
struct Inner: public A<B>::Inner
{int b = 20;};
protected:
friend A<B>;
B::Inner inner;
public:
void dispInner(void) const
{
A<B>::dispInner();
std::cout << asLeaf()->inner.b << std::endl;
}
};
int main()
{
B b;
b.dispInner();
return 0;
}
修改
我想根据收到的反馈提供一些进一步的评论:
A
是否应该知道inner
的存在。但是,我想在inner
中定义B::Inner
类型的对象A
,而不是在inner
中提供B
的定义,并在A
中使用它{1}}。B
和/或B::Inner
以及无法完成此操作的原因。因此,从技术上讲,设计问题没有解决方案。但是,我正在寻找一种可行的解决方法。我已经考虑了几种替代解决方案:
B::Inner inner
中“定义”A
并使用A
的成员函数来提供允许修改{的功能{1}} A<B>::Inner
的一部分。 B::Inner inner
和A<B>::Inner
(即不是嵌套类)。但是,我宁愿避免这种情况,因为根据设计,不期望任何不从B::Inner
派生的类需要与A
或从{{1}派生的类进行交互。 }} 我提出的两种解决方案都是可以接受的。但是,我正在寻找任何可行的替代方案。
答案 0 :(得分:1)
标准说:
在类说明符的结束
}
,类被视为完全定义的对象类型(或完整类型)。
因此,B
将A
专门化为A<B>
时,A
不是完全定义的对象。因此,您无法期望能够从A
的定义中访问其成员或类型或任何内容(即使您可以在{{的成员方法的定义中回调派生类) 1}},这是完全合法的,而不是CRTP习语的目的。)
换句话说,当你这样做时:
typename T::Inner mvInnerA
您无法保证T
是一个完全定义的对象,这也是您收到错误的原因。
一些替代方案:
您可以将mvInnerType
定义为函数而不是类型,并将其用作工厂来创建T::inner
类型的对象:
[static] auto mvInnerA() {
return typename T::Inner{};
}
将其用作:
auto foo = A<B>::mvInnerA();
或者:
auto foo = obj.mvInnerA();
正确的形式取决于您是否static
。{
请注意,您仍然可以以某种方式使用隐藏类型,即使其名称不可访问:
using HiddenType = decltype(A<B>::mvInnerA());
HiddenType foo = A<B>::mvInnerA();
您可以使用模板定义mvInnerA
,如下所示:
template<typename U = T>
using mvInnerA = typename U::Inner;
然后将其用作:
auto foo = A<B>::mvInnerA<>{};
对于类型T
是(我说)仅在实例U
时通过mvInnerA
间接使用你没有上面提到的问题。付费的代价是存在恼人的<>
以及可以将自定义类型传递给mvInnerA
的事实。
答案 1 :(得分:0)
如何使用内部类型的CRTP模板参数受到严格限制。
类模板定义本身的范围没有用处。在实例化模板时,需要完全定义类型B
,类似于skypjack points out,它不是。但是,您可以在不使用类模板实例化的上下文中使用它,类模板主要是A
的成员函数。
虽然您不能拥有B::Inner
的类型别名,但您可以拥有类型别名模板
template<class C>
using Inner = typename C::Inner
A
个成员函数可用于避免typename B::Inner
的详细程度,而是使用Inner<B>
。