Почему функция шаблона элемента данных является зависимым именем, только если оно соответствует условию & quot; this & quot ;?

struct Bar {
template<typename>
void baz() {
}
};

template<typename>
struct Foo {
Bar bar;

Foo() {
bar.baz<int>();
}
};

int main() {
return 0;
}

Этот код компилируется нормально (в GCC 4.7), но если я префикс вызова bar.baz<int>() с this->, baz становится зависимым именем, которое нуждается в устранении неоднозначности template,

bar.baz<int>(); // OK
this->bar.baz<int>(); // error
this->bar.template baz<int>(); // OK

конечно this->bar может относиться только к Bar barчей член baz это явно шаблон? Почему добавление this-> сделать этот код неоднозначным для компилятора?

постскриптум Первоначально, bar был членом данных шаблона базового класса, который требовал устранения неоднозначности с this->, но я упростил пример для целей этого вопроса.

14

Решение

this->bar.baz<int>(); // error

Заявление выше, в рамках определения template<typename T> Foo<T>::Foo(), правильно сформирован и должен приниматься, если включен режим C ++ 11 или режим C ++ 1y. Но это было технически плохо сформировано согласно C ++ 03.

Оба стандарта согласны с тем, что this является зависимым от типа выражением:

С ++ 03 14.6.2.1/1; N3690 14.6.2.1/8:

Тип зависит, если он

  • параметр шаблона,

  • [просто-]Шаблон-идентификатор в котором либо имя шаблона является параметром шаблона, либо любой из аргументов шаблона является зависимым типом или выражением, которое зависит от типа или значения,

[T является зависимым типом, и поэтому Foo<T>.]

С ++ 03 / N3690 14.6.2.2/2:

this зависит от типа, если тип класса вмещающей функции-члена является зависимым.

[Поскольку Foo<T> это зависимый тип, выражение this в его определении члена зависит от типа.]

Оба стандарта начинаются с 14.6.2.2:

За исключением случаев, описанных ниже, выражение зависит от типа, если любое подвыражение зависит от типа.

C ++ 03 имеет только три простые категории выражений с более точными описаниями:

  • Основные выражения (this и подыскивал имена)

  • Выражения, которые определяют их собственный тип (например, приведение типов и новые выражения)

  • Выражения с постоянным типом (например, литералы и sizeof).

Первая категория определена в C ++ 03 14.6.2.2/3:

ID-выражение зависит от типа, если оно содержит:

  • идентификатор который был объявлен с зависимым типом,

  • Шаблон-идентификатор это зависит,

  • преобразование-функция-идентификатор который определяет зависимый тип,

  • вложенное имя спецификатор который содержит Имя_класса который называет зависимый тип.

Так что одинокое выражение bar не зависит: это идентификатор и ID-выражение, но ничего из вышеперечисленного не применимо.

Но this->bar это не ID-выражение, или в любом другом исключении C ++ 03, поэтому мы должны следовать правилу подвыражения. С подвыражением this зависит от типа, содержащее выражение this->bar также зависит от типа.

Но на самом деле, как вы заметили, тип this->bar может быть известно при разборе определения шаблона, без создания каких-либо аргументов шаблона. Он объявлен как член основного шаблона, поэтому имя должно быть связано с этим объявлением члена. Специализация шаблона может сделать Foo<T>::bar необъявленный или объявленный другим способом, но в этом случае основной шаблон не будет использоваться вообще, а текущее определение Foo() игнорируется для этой специализации. Вот почему C ++ 11 определил понятие «текущая реализация» и использовал его для дальнейшего исключения из заразности зависимых от типа выражений.

N3690 14.6.2.1/1:

Название относится к текущая реализация если это

  • в определении шаблона класса, вложенный класс шаблона класса, член шаблона класса или член вложенного класса шаблона класса, имя введенного класса шаблона класса или вложенного класса

  • в определении шаблона первичного класса или члена шаблона первичного класса, имя шаблона класса, за которым следует список аргументов шаблона первичного шаблона (как описано ниже), заключенный в <> (или эквивалентная специализация псевдонима шаблона),

[Первая пуля говорит Foo текущая реализация Второй говорит Foo<T> текущая реализация В этом примере оба имени имеют одинаковый тип.]

14.6.2.1/4:

Имя это член текущей инстанции если это

  • Неквалифицированное имя, которое при поиске относится, по крайней мере, к одному члену класса, который является текущим экземпляром, или его независимому базовому классу.

  • Квалифицированный-идентификатор в котором …

  • ID-выражение обозначение члена в выражении доступа к члену класса, для которого тип выражения объекта является текущей реализацией, и ID-выражение, при поиске относится, по крайней мере, к одному члену класса, который является текущим экземпляром, или его независимому базовому классу.

[Первая пуля говорит bar один является членом текущего экземпляра. Третья пуля говорит this->bar является членом текущего экземпляра.]

Наконец, C ++ 11 добавляет четвертую категорию правил для зависимых от типа выражений для доступа к элементу. 14.6.2.2/5:

Выражение доступа к члену класса зависит от типа, если выражение относится к члену текущего экземпляра и тип ссылочного члена является зависимым, или выражение доступа к члену класса относится к члену неизвестной специализации.

this->bar ссылается на член текущего экземпляра, но тип Bar от указанного члена не зависит. А сейчас this->bar не зависит от типа, а имя baz в this->bar.baz ищется во время определения шаблона как независимое имя. template Ключевое слово не нужно раньше baz,

4

Другие решения

Краткое содержание

Вот как текущие правила C ++ 11: this->bar.baz<int>() вводит зависимое имя не в текущем контексте реализации, который требует устранения неоднозначности с template ключевое слово, хотя очень трудно найти реальный пример конкурирующего анализа, который изменяет семантику выражения this->bar.baz<int>(),

Разбор двусмысленности от угловых скобок

Во-первых: зачем вообще нужна template?

Когда компилятор C ++ встречает выражение f<g>(0), он может интерпретировать это как «вызов шаблона функции f для аргумента шаблона g и аргумент функции 0 и оценить результат «или это может означать» сделать сравнение (f<g)>(0) для имен f а также g и постоянный 0. «Без дополнительной информации он не может принять это решение. Это является печальным следствием выбора угловых скобок для аргументов шаблона.

Во многих (большинстве) случаях компилятор делает иметь достаточно контекста, чтобы решить, анализируется ли выражение шаблона или сравнение. Тем не менее, когда так называемый зависимое имя (по сути, встречается имя, которое явно или неявно зависит от параметра шаблона текущей области), вступает в игру другая языковая тонкость.

Двухфазный поиск имени

Поскольку имя, зависящее от шаблона, может изменить свое значение (например, посредством специализаций), когда создается экземпляр шаблона для конкретного типа, поиск имени зависимых имен выполняется в два этапа (цитата из Шаблоны C ++ Полное руководство):

На первом этапе ищутся независимые имена, а
шаблон анализируется с использованием как обычных правил поиска, так и, если
применимы правила для аргументно-зависимого поиска (ADL).
Неквалифицированные зависимые имена (которые являются зависимыми, потому что они выглядят как имя функции в вызове функции с зависимой
аргументы) также смотрятся таким образом, но результат поиска
не считается завершенным, пока не будет выполнен дополнительный поиск
когда шаблон создан
.

На втором этапе, который происходит, когда создаются шаблоны
в точке, называемой точкой инстанции (POI), зависимой
поиск квалифицированных имен (с заменой параметров шаблона)
с аргументами шаблона для этого конкретного экземпляра), и
дополнительный ADL выполняется для неквалифицированных зависимых имен.

Зачем this-> делает ваш код другим

С помощью this-> внутри шаблона класса вводится зависимое имя и запускается двухфазный поиск имени. В этом случае вступает в силу правило, цитируемое @ 40two. Дело в том, что ADL на 2-м этапе может вводить новые имена из явных специализаций, которые переопределяют значение вашего bar а также baz и это может предположительно изменить значение this->bar.baz<int>(0) для сравнения, а не вызова шаблона функции.

Конечно, для не типовых аргументов шаблона, таких как this->bar.another_baz<0>() это было бы более вероятно, чем для параметра шаблона типа. В это связано Q& аналогичное обсуждение возникло, можно ли найти синтаксическую действительную форму, которая изменяет значение this->f<int>() против this->template f<int>(0)без четкого заключения.

Обратите внимание, что C ++ 11 уже ослабляет правило для template неоднозначность по сравнению с C ++ 98. По действующим правилам this->f<int>() для template<class> f() внутри Foo не требует template потому что это в так называемом текущая реализация. Увидеть этот ответ от канонический Q& по этой теме для более подробной информации

3

В соответствии со стандартом § 14.2 / 4 Названия шаблонных специализаций [temp.names]

Когда имя специалиста шаблона члена появляется после . или же -> в выражении post-x или после вложенного-name-specer-а в квалифицированном id, а объектное выражение post-x-expression зависит от типа, или nested-name-specer в квалифицированном-id ссылается на зависимый тип, но имя не является членом текущего экземпляра (14.6.2.1), перед именем шаблона элемента должно стоять ключевое слово template,

Редактировать:

Также по стандарту § 14.6.2.2/2 Типозависимые выражения [temp.dep.expr]:

this зависит от типа, если тип класса функции-члена является зависимым (14.6.2.1).

Таким образом, чтобы позвонить bar.baz<int>() с помощью this вам нужно префикс по ключевому слову template:

this->bar.template baz<int>();

LIVE DEMO

[Причина:] Компилятору нужно это «избыточное» использование template ключевое слово, потому что не может решить, является ли токен < является operator< или начало списка аргументов шаблона.

2
По вопросам рекламы [email protected]