模板化继承中的范围解析(可能称为混合)

时间:2019-03-04 16:17:04

标签: c++ templates scope-resolution

假设我有模板化的类

#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;
}

在模板化类A4的定义中,至少在仅使用实例类型A4<A3<A2<A1>>>的情况下,似乎可以将x称为两者之一

this->A1::x;

Context::A1::x;

A1::x;

问题1:这些等效吗?好吧,我认为从单独查看的模板化类A4的角度来看,我认为它们并不等效。为了使Context::A1::x工作,其模板参数应包含x。为了使this->A1::x工作,它应该包含一个名为A1的范围,而该范围又应包含一个x。为了使A1::x工作,A4本身的范围应包含一个名为A1的范围,其中包含一个x我的目的是从类型的角度询问它们是否等效 A4<A3<A2<A1>>>

Nota bene:带有-O03 -std=c++17的gcc 8.2在每种情况下都会产生相同的汇编代码。即,我仅使用函数funcgunchunc中的一个编译了代码,并且仅对相应的一个调用进行了编译,并且此编译器生成了相同的可执行文件。当然,严格来说,这不一定意味着对于抽象语言而言,这些表达是等效的。

问题2:在每种情况下,x范围的“拆包”如何工作?也许这个问题没有道理,或者不完全是我要问的问题。特别是如果对问题1的回答是等同的。在找到有关问题1的更多信息之后,请允许我修改此问题,或者先忽略此问题。

问题2:的注意事项可能会阐明为什么我不确定拆包的工作方式。如果在模板化类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而言,其template参数的template参数不是有效名称(或者我没有为其命名)正确地放在Context::Context::A1::x中。

2 个答案:

答案 0 :(得分:2)

在这种情况下,我认为您正在继承(使用模板)。因此,Context :: x引用了父对象的x属性。在这种情况下,由于A3不会覆盖此属性,因此与A1 :: x相同。 在第二个(gunc)中,您直接使用“ this”引用A1,因此没有问题。 在第三个(hunc,未使用)中,gunc带有对self的隐式引用。 (但我不确定)

此外,如果您添加A2类:

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 :(得分:1)

问题1和2:

所有版本对于您选择的实例都是等效的。只要它没有歧义,就可以直接使用成员x,而无需指定范围。如果该成员不在当前类中,则检查基类,然后进行进一步检查。

如果您指定了特定的基类,但成员x不存在,则会再次查询基类。

对于您的特殊专业,您有

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;
}

编译器现在抱怨类A3<A2<A1>>中没有typedef,该类引入了名称Context。 template参数仅在类模板中可见。