模板继承中的范围解析(可能是所谓的 mixin)
Scope resolution in templated inheritance (possibly what is called mixin)
假设我有模板 classes
#include <iostream>
class A1 {
public:
int x{314159};
};
template<typename Context>
class A2 : public Context {};
template<typename Context>
class A3 : public Context {};
template<typename Context>
class A4 : public Context {
public:
int func() {
return Context::A1::x;
}
int gunc() {
return this->A1::x;
}
int hunc() {
return A1::x;
}
};
int main() {
A4<A3<A2<A1>>> my_A;
std::cout << "x = func() = " << my_A.func() << std::endl;
std::cout << "x = gunc() = " << my_A.gunc() << std::endl;
std::cout << "x = hunc() = " << my_A.hunc() << std::endl;
return 0;
}
模板化的定义里面classA4
,至少在只使用实例类型A4<A3<A2<A1>>>
的时候,好像可以参考x
作为
this->A1::x;
或
Context::A1::x;
或
A1::x;
问题一:这些是等价的吗?好吧,我认为从模板化 class A4
孤立来看的角度来看,它们并不等同。要使 Context::A1::x
正常工作,其模板参数应包含一个 x
。要使 this->A1::x
正常工作,它应该包含一个名为 A1
的范围,而该范围又应该包含一个 x
。为了使 A1::x
起作用,A4
本身的作用域应该包含一个名为 A1
的作用域,其中包含一个 x
。 我的本意是问从类型的角度看是否等价 A4<A3<A2<A1>>>
.
注意事项: gcc 8.2 和 -O03 -std=c++17
在每种情况下都生成相同的汇编代码。也就是说,我只使用函数 func
、gunc
和 hunc
之一编译代码,并且只调用相应的一个函数,并且此编译器生成了相同的可执行文件。当然,严格来说,这并不一定意味着对于抽象语言而言,这些表达式是等价的。
问题 2:x
范围的 'unpacking' 在每种情况下如何工作?也许这个问题没有意义或者不完全是我想问的。特别是如果问题 1 的答案是它们是等价的。请允许我在找到有关问题1的更多信息后修改此问题,或者先忽略此问题。
问题 2 的注释:这个观察结果可能会解释为什么我不确定解包是如何工作的。如果在模板化 class A4
中我们还有一个方法
int iunc() {
return Context::Context::A1::x;
}
然后编译失败
memberTemplatedParent.cpp: In instantiation of ‘int A4<Context>::iunc() [with Context = A3<A2<A1> >]’:
memberTemplatedParent.cpp:48:45: required from here
memberTemplatedParent.cpp:37:22: error: no type named ‘Context’ in ‘class A3<A2<A1> >’
return Context::Context::A1::x;
^
所以,至少 gcc
在创建 A4
的类型实例时,其模板参数的模板参数不是有效名称(或者我没有在 Context::Context::A1::x
).
中正确命名
在那种情况下,我认为您正在继承(使用模板)。所以 Context::x 指的是父级的 x 属性。在那种情况下,A3,因为 A3 不会覆盖这个 属性,所以你和 A1::x 一样。
在第二个 (gunc) 中,您使用 "this" 直接引用 A1,因此没问题。
在第三个(hunc,未被如此使用)中,相同的是 gunc 与对自我的隐含引用。 (但我不完全确定)
此外,如果您在 A2 中添加 class:
template<typename Context>
class A2 : public Context {
public :
int x{45678};
};
第一个将打印“45678”
如果现在在保留 A2 的同时加入 A3
template<typename Context>
class A3 : public Context {
public :
int x{67890};
};
第一个输出将是 67890
问题 1 和问题 2:
对于您选择的实例化,所有版本都是等效的。只要不引起歧义,可以不指定作用域,直接使用成员x
。如果该成员不在当前 class 中,则检查基础 class,依此类推。
如果您指定了一个特定的基 class 而成员 x
不存在,则会再次查询基 class。
对于您的特定专业,您有
class A2<A1> : public A1 {};
class A3<A2<A1>> : public A2<A1>{};
class A4<A3<A2<A1>>> : public A3<A2<A1>> {
public:
int func() {
return A3<A2<A1>>::A1::x; // fine: search for x in A1,
// where A1 is searched in A3<A2<A1>>
}
int gunc() {
return this->A1::x; // fine: why not specifying A1 directly. The this pointer
// is not required here but adding does not cause any harm.
}
int hunc() {
return A1::x; // fine: why not specifying A1 directly.
}
int iunc() {
return x; // fine: would be possible as well
}
};
最后一题:
int iunc() {
return Context::Context::A1::x;
}
模板实例化后如下
int iunc() {
return A3<A2<A1>>::Context::A1::x;
}
编译器现在抱怨 class A3<A2<A1>>
中没有引入名称 Context
的 typedef。模板参数仅在 class 模板中可见。
假设我有模板 classes
#include <iostream>
class A1 {
public:
int x{314159};
};
template<typename Context>
class A2 : public Context {};
template<typename Context>
class A3 : public Context {};
template<typename Context>
class A4 : public Context {
public:
int func() {
return Context::A1::x;
}
int gunc() {
return this->A1::x;
}
int hunc() {
return A1::x;
}
};
int main() {
A4<A3<A2<A1>>> my_A;
std::cout << "x = func() = " << my_A.func() << std::endl;
std::cout << "x = gunc() = " << my_A.gunc() << std::endl;
std::cout << "x = hunc() = " << my_A.hunc() << std::endl;
return 0;
}
模板化的定义里面classA4
,至少在只使用实例类型A4<A3<A2<A1>>>
的时候,好像可以参考x
作为
this->A1::x;
或
Context::A1::x;
或
A1::x;
问题一:这些是等价的吗?好吧,我认为从模板化 class A4
孤立来看的角度来看,它们并不等同。要使 Context::A1::x
正常工作,其模板参数应包含一个 x
。要使 this->A1::x
正常工作,它应该包含一个名为 A1
的范围,而该范围又应该包含一个 x
。为了使 A1::x
起作用,A4
本身的作用域应该包含一个名为 A1
的作用域,其中包含一个 x
。 我的本意是问从类型的角度看是否等价 A4<A3<A2<A1>>>
.
注意事项: gcc 8.2 和 -O03 -std=c++17
在每种情况下都生成相同的汇编代码。也就是说,我只使用函数 func
、gunc
和 hunc
之一编译代码,并且只调用相应的一个函数,并且此编译器生成了相同的可执行文件。当然,严格来说,这并不一定意味着对于抽象语言而言,这些表达式是等价的。
问题 2:x
范围的 'unpacking' 在每种情况下如何工作?也许这个问题没有意义或者不完全是我想问的。特别是如果问题 1 的答案是它们是等价的。请允许我在找到有关问题1的更多信息后修改此问题,或者先忽略此问题。
问题 2 的注释:这个观察结果可能会解释为什么我不确定解包是如何工作的。如果在模板化 class A4
中我们还有一个方法
int iunc() {
return Context::Context::A1::x;
}
然后编译失败
memberTemplatedParent.cpp: In instantiation of ‘int A4<Context>::iunc() [with Context = A3<A2<A1> >]’:
memberTemplatedParent.cpp:48:45: required from here
memberTemplatedParent.cpp:37:22: error: no type named ‘Context’ in ‘class A3<A2<A1> >’
return Context::Context::A1::x;
^
所以,至少 gcc
在创建 A4
的类型实例时,其模板参数的模板参数不是有效名称(或者我没有在 Context::Context::A1::x
).
在那种情况下,我认为您正在继承(使用模板)。所以 Context::x 指的是父级的 x 属性。在那种情况下,A3,因为 A3 不会覆盖这个 属性,所以你和 A1::x 一样。 在第二个 (gunc) 中,您使用 "this" 直接引用 A1,因此没问题。 在第三个(hunc,未被如此使用)中,相同的是 gunc 与对自我的隐含引用。 (但我不完全确定)
此外,如果您在 A2 中添加 class:
template<typename Context>
class A2 : public Context {
public :
int x{45678};
};
第一个将打印“45678”
如果现在在保留 A2 的同时加入 A3
template<typename Context>
class A3 : public Context {
public :
int x{67890};
};
第一个输出将是 67890
问题 1 和问题 2:
对于您选择的实例化,所有版本都是等效的。只要不引起歧义,可以不指定作用域,直接使用成员x
。如果该成员不在当前 class 中,则检查基础 class,依此类推。
如果您指定了一个特定的基 class 而成员 x
不存在,则会再次查询基 class。
对于您的特定专业,您有
class A2<A1> : public A1 {};
class A3<A2<A1>> : public A2<A1>{};
class A4<A3<A2<A1>>> : public A3<A2<A1>> {
public:
int func() {
return A3<A2<A1>>::A1::x; // fine: search for x in A1,
// where A1 is searched in A3<A2<A1>>
}
int gunc() {
return this->A1::x; // fine: why not specifying A1 directly. The this pointer
// is not required here but adding does not cause any harm.
}
int hunc() {
return A1::x; // fine: why not specifying A1 directly.
}
int iunc() {
return x; // fine: would be possible as well
}
};
最后一题:
int iunc() {
return Context::Context::A1::x;
}
模板实例化后如下
int iunc() {
return A3<A2<A1>>::Context::A1::x;
}
编译器现在抱怨 class A3<A2<A1>>
中没有引入名称 Context
的 typedef。模板参数仅在 class 模板中可见。