6

假设我有模板类

#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其自身的范围应该包含一个名为A1contains的范围x我的意图是从 type 的角度询问它们是否等效 A4<A3<A2<A1>>>

注意: gcc 8.2 with-O03 -std=c++17在每种情况下都产生相同的汇编代码。也就是说,我只用函数func,gunchunc只调用了对应的函数之一来编译代码,这个编译器生成了相同的可执行文件。当然,严格来说,这并不一定意味着对于抽象语言,这些表达式是等价的。

问题二: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它的模板参数的模板参数不是一个有效的名称(或者我没有在 中正确命名Context::Context::A1::x)。

4

2 回答 2

2

在那种情况下,我认为您正在进行继承(使用模板)。所以 Context::x 指的是父级的 x 属性。在这种情况下 A3,由于 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

于 2019-03-04T16:39:43.083 回答
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>>编译器现在抱怨在引入 name的类中没有 typedef Context。模板参数仅在类模板中可见。

于 2019-03-04T22:38:10.010 回答