Разрешение области в шаблонном наследовании (возможно, то, что называется mixin) - PullRequest
0 голосов
/ 04 марта 2019

Предположим, у меня есть шаблонные классы

#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>>>.

Примечание: gcc 8.2 с -O03 -std=c++17 производитодин и тот же код сборки в каждом случае.А именно, я скомпилировал код только с одной из функций func, gunc и hunc и только одним вызовом соответствующей функции, и этот компилятор создал идентичные исполняемые файлы.Конечно, строго говоря, это не обязательно означает, что для абстрактного языка эти выражения эквивалентны.

Вопрос 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, параметр шаблона его параметра шаблона не является допустимым именем (или я неправильно назвал его в Context::Context::A1::x).

Ответы [ 2 ]

0 голосов
/ 05 марта 2019

Вопросы 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.Параметр шаблона виден только внутри шаблона класса.

0 голосов
/ 04 марта 2019

В этом случае я думаю, что вы делаете наследование (используя шаблон).Поэтому Context :: x ссылается на свойство x родителя.в этом случае A3, поскольку A3 не перезаписывает это свойство, у вас есть то же самое, что A1 :: x.Во втором (gunc) вы обращаетесь непосредственно к A1, используя «this», поэтому проблем нет.В третьем (hunc, который так не используется) это тот же gunc с косвенной ссылкой на себя.(но я не совсем уверен)

Также, если вы добавите в класс A2:

template<typename Context>
class A2 : public Context {
public :
    int x{45678};
};

Первый из них напечатает «45678»

Если теперь выдобавить в A3, сохраняя A2

template<typename Context>
class A3 : public Context {
public :
    int x{67890};
};

, первый результат будет 67890

...