模板继承中的范围解析(可能是所谓的 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 在每种情况下都生成相同的汇编代码。也就是说,我只使用函数 funcgunchunc 之一编译代码,并且只调用相应的一个函数,并且此编译器生成了相同的可执行文件。当然,严格来说,这并不一定意味着对于抽象语言而言,这些表达式是等价的。

问题 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 模板中可见。