CRTP - 嵌套叶类型的可见性 class
CRTP - visibility of the type of a nested leaf class
我想了解是否可以在基本 CRTP class 中使用叶 CRTP class 的嵌套 class。下面的例子演示了这个问题。
#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
的存在。但是,我想在 A
中定义类型为 B::Inner
的对象 inner
,而不是在 B
中提供 inner
的定义并在 A
.
- 我知道我不能转发声明
B
and/or B::Inner
以及不能这样做的原因。因此,从技术上讲,设计问题没有解决方案。但是,我正在寻找可行的解决方法。
我已经考虑过几种替代方案:
- 一个可行的解决方案是不在
A
中尝试'define' B::Inner inner
,而是使用A
的成员函数来提供允许的功能修改 B::Inner inner
的 A<B>::Inner
部分。
- 另一种可能的解决方案是明确定义 classes
A<B>::Inner
和 B::Inner
(即不是嵌套的 classes)。但是,我宁愿避免这种情况,因为根据设计,不希望任何不是从 A
派生的 classes 需要与 A<B>::Inner
或 classes 派生自 A<B>::Inner
我提出的两种解决方案都可以接受。但是,我正在寻找任何可行的替代方案。
标准说:
A class is considered a completely-defined object type (or complete type) at the closing }
of the class-specifier.
因此当您将 A
特化为 A<B>
时,B
不是一个完全定义的对象。因此,您不能期望能够从 A
的定义中访问其成员或类型或任何内容(即使您可以从 class 的成员方法的定义中回调派生的 class A
,除了 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
是(让我说) 仅在实例化 mvInnerA
时通过 U
间接使用,你不需要没有上面提到的问题。为此付出的代价是存在烦人的 <>
以及可以将自定义类型传递给 mvInnerA
.
的事实
如何使用 CRTP 模板参数的内部类型受到严格限制。
在class 模板定义本身的范围内是没有用的。实例化模板时,需要完全定义类型 B
,而 则不是。但是,您可以在未立即使用 class 模板实例化的上下文中使用它,该模板主要是 A
.
的成员函数
虽然您不能为 B::Inner
设置类型别名,但您可以使用类型别名模板
template<class C>
using Inner = typename C::Inner
可以使用A
的成员函数来避免typename B::Inner
的冗长,而是使用Inner<B>
.
我想了解是否可以在基本 CRTP class 中使用叶 CRTP class 的嵌套 class。下面的例子演示了这个问题。
#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
的存在。但是,我想在A
中定义类型为B::Inner
的对象inner
,而不是在B
中提供inner
的定义并在A
. - 我知道我不能转发声明
B
and/orB::Inner
以及不能这样做的原因。因此,从技术上讲,设计问题没有解决方案。但是,我正在寻找可行的解决方法。
我已经考虑过几种替代方案:
- 一个可行的解决方案是不在
A
中尝试'define'B::Inner inner
,而是使用A
的成员函数来提供允许的功能修改B::Inner inner
的A<B>::Inner
部分。 - 另一种可能的解决方案是明确定义 classes
A<B>::Inner
和B::Inner
(即不是嵌套的 classes)。但是,我宁愿避免这种情况,因为根据设计,不希望任何不是从A
派生的 classes 需要与A<B>::Inner
或 classes 派生自A<B>::Inner
我提出的两种解决方案都可以接受。但是,我正在寻找任何可行的替代方案。
标准说:
A class is considered a completely-defined object type (or complete type) at the closing
}
of the class-specifier.
因此当您将 A
特化为 A<B>
时,B
不是一个完全定义的对象。因此,您不能期望能够从 A
的定义中访问其成员或类型或任何内容(即使您可以从 class 的成员方法的定义中回调派生的 class A
,除了 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
是(让我说) 仅在实例化mvInnerA
时通过U
间接使用,你不需要没有上面提到的问题。为此付出的代价是存在烦人的<>
以及可以将自定义类型传递给mvInnerA
. 的事实
如何使用 CRTP 模板参数的内部类型受到严格限制。
在class 模板定义本身的范围内是没有用的。实例化模板时,需要完全定义类型 B
,而 A
.
虽然您不能为 B::Inner
设置类型别名,但您可以使用类型别名模板
template<class C>
using Inner = typename C::Inner
可以使用A
的成员函数来避免typename B::Inner
的冗长,而是使用Inner<B>
.