Поиск имени в базе шаблонов: почему мы добавляем это-> - PullRequest
4 голосов
/ 11 ноября 2019

Рассмотрим следующее:

struct Base {
    void foo();
};

template <class T>
struct Derived : Base {
    void bar() {
        this->foo();
    }
};

Как правило, мы объясняем, что this-> делает foo() зависимым именем, поэтому его поиск откладывается до второй фазы, то есть до точки создания шаблона.

Но поиск второй фазы вызывает only ADL, который не учитывает функции-члены, не так ли?

Я был бы признателен за любой указатель на абзац Standard, который объясняет, почемукод выше компилируется.

Ответы [ 2 ]

7 голосов
/ 11 ноября 2019

Но поиск второй фазы вызывает только ADL, который не учитывает функции-члены, не так ли?

Это не так. Однако ADL добавляет к поисковому набору, он не включает в себя все это. Кроме того, идея, которую вы здесь перефразируете, применима к постфиксному выражению в postfix-expression(args), когда указанное постфиксное выражение представляет собой безусловный идентификатор .

[temp.dep]

1 ... В выражении вида:

postfix-expression ( expression-list<sub>opt</sub> )

, где постфиксным выражением является безусловный идентификатор,unqualified-id обозначает зависимое имя, если

  • [... особые условия ...]

Если операнд оператора является зависимым от типа выражением,Оператор также обозначает зависимое имя. Такие имена не связаны и ищутся в точке создания шаблона ([temp.point]) как в контексте определения шаблона, так и в контексте точки создания.

Так что есливместо этого у вас было foo(), при поиске не учитывались бы члены, а вместо этого использовались бы только свободные функции, как в точке определения, так и в экземпляре (где ADL можно добавить в набор поиска, предполагая, что у нас было зависимое выражение).

Но для this->foo (я намеренно пропустил вызов, чтобы обсудить выражение postfix), у нас есть доступ к членам класса. И здесь применяются другие параграфы:

[temp.dep.type]

5 Имя является членом текущего экземпляраесли это

  • [...]
  • id-выражение, обозначающее член в выражении доступа к члену класса, для которого тип выражения объекта является текущей реализацией, ивыражение id при поиске относится, по крайней мере, к одному члену класса, который является текущим экземпляром, или его независимому базовому классу. [Примечание: если такой член не найден, а текущая реализация имеет какие-либо зависимые базовые классы, тогда id-выражение является членом неизвестной специализации;Смотри ниже. - примечание к концу]

6 Имя является членом неизвестной специализации, если оно

  • [...]
  • Идентификационное выражение, обозначающее член в выражении доступа к члену класса, в котором либо
    • тип выражения объекта является текущим экземпляром, текущий экземпляр имеет по крайней мере один зависимый базовый класс, и поиск имениid-выражение не находит члена класса, который является текущим экземпляром, или его независимого базового класса;или

7 Аналогично, если id-выражение в выражении доступа к члену класса, для которого тип выражения объекта является текущим, нессылаясь на члена текущего экземпляра или члена неизвестной специализации, программа некорректна, даже если шаблон, содержащий выражение доступа члена, не создан;Диагностика не требуется.

Эти маркеры говорят нам, какой поиск нужно выполнить при обнаружении this->foo. Он смотрит только на членов. В нашем случае у нас есть независимый базовый класс в текущем экземпляре, так что именно здесь должен быть найден член, однозначно.

Найдя функцию-член, постфиксное выражение this->foo обозначает вызываемый объект, и именно так разрешается вызов функции.

2 голосов
/ 11 ноября 2019

Фраза «двухфазный поиск» часто неправильно понимается даже среди экспертов. C ++ 17 сказал, что оба

Такие имена не связаны и ищутся в точке создания шаблона (17.6.4.1) как в контексте определения шаблона, так и в контексте точкисоздание экземпляров.

(в [temp.dep] / 1) и

При разрешении зависимых имен учитываются имена из следующих источников:
- Объявления, которыевидимы в точке определения шаблона.
- Объявления из пространств имен, связанных с типами аргументов функции, как из контекста экземпляра (17.6.4.1), так и из контекста определения.

(в [temp.dep.res] / 1), каждая из которых, кажется, предполагает, что это две фазы. Однако лучший способ интерпретации этой (неофициальной) фразы заключается в том, что поиск выполняется

  1. из шаблона определение : для независимых имен
  2. во время экземпляр : из определения, но также и только через ADL, из контекста экземпляра

Первый можно сделать при синтаксическом анализеопределение шаблона, но для неосуществимых шаблонов диагностика не требуется ([temp.res] / 8). Обратите внимание, что последний включает в себя более ADL.

Соответствующая формулировка была недавно уточнена;теперь он просто говорит

[ Примечание : для части поиска, использующей связанные пространства имен ([basic.lookup.argdep]), объявления функций, найденные в контексте создания шаблона,найденный этим поиском, как описано в [basic.lookup.argdep]. - конечная нота ]

как напоминание о разнице во второй фазе. Тем не менее, он вводит в заблуждение зависимое имя только для ссылки на неквалифицированные зависимые имена / операторы ([temp.dep] / 2). Имена типа Derived::foo (от this->) явно зависят ;как квалифицированные имена, они не подлежат ADL, но они все еще ищутся во время создания экземпляра, что позволяет находить результаты из зависимой базы. (Ваш пример имеет независимую базу, но она также доступна.)

...