Ответ 1
Насколько мне известно, что происходит.
DR228 говорит:
[Проголосовал за WP на собрании в апреле 2003 года.]
Рассмотрим следующий пример:
template<class T>
struct X {
virtual void f();
};
template<class T>
struct Y {
void g(X<T> *p) {
p->template X<T>::f();
}
};
Это ошибка, потому что X не является шаблоном-членом; 14.2 [temp.names] в пункте 5 говорится:
Если имя, предваряемое шаблоном ключевого слова, не является именем шаблона-члена, программа плохо сформирована.
В некотором смысле это имеет смысл: X считается шаблоном с использованием обычного поиска, хотя p имеет зависимый тип. Тем не менее, я думаю, что это делает использование префикса шаблона еще более трудным для обучения.
Было ли это намеренно объявлено вне закона?
Предлагаемое решение (4/02):
Исключить первое использование слова "член" в пункте 14.2 [temp.names], чтобы его первое предложение гласило:
Если имя, предваряемое шаблоном ключевого слова, не является именем шаблона, программа плохо сформирована.
Однако в самом современном общедоступном проекте стандарта С++ N4296 в §14.2.5 появляется следующая формулировка:
Имя, предваряемое шаблоном ключевого слова, должно быть идентификатором шаблона, или имя должно ссылаться на шаблон класса. [Примечание. Ключевое слово
template
может не применяться к элементам шаблонов классов без шаблонов. -end note] [Примечание: как и в случае с префиксомtypename
, префиксtemplate
допускается в случаях, когда это не является строго необходимым; то есть, когда спецификатор вложенного имени или выражение слева от->
или.
не зависит от параметра шаблона, или использование не отображается в области шаблона. -end note][Пример:
template <class T> struct A {
void f(int);
template <class U> void f(U);
};
template <class T> void f(T t) {
A<T> a;
a.template f<>(t); // OK: calls template
a.template f(t); // error: not a template-id
}
template <class T> struct B {
template <class T2> struct C { };
};
// OK: T::template C names a class template:
template <class T, template <class X> class TT = T::template C> struct D { };
D<B<int> > db;
-end пример]
Эта формулировка звучала аналогично, но достаточно различна, чтобы копать. Я обнаружил, что в проекте N3126 текст был изменен на эту версию.
Мне удалось связать это изменение с этим DR96:
Ниже приведена формулировка из 14.2 [temp.names] абзацев 4 и 5, в которой обсуждается использование ключевого слова "шаблон" . или → и в квалифицированных именах.
{надрез}
Весь смысл этой функции состоит в том, чтобы сказать, что ключевое слово "шаблон" необходимо для указания того, что "<" начинает список параметров шаблона в определенных контекстах. Ограничения в параграфе 5 оставляют открытыми для обсуждения определенных случаев.
Во-первых, я думаю, должно быть более ясно, что вместо имени шаблона должен следовать список аргументов шаблона, когда в этих контекстах используется ключевое слово "шаблон" . Если мы этого не сделаем, нам придется добавить несколько семантических разъяснений. Например, если вы скажете "p- > template f()", а "f" - это набор перегрузки, содержащий как шаблоны, так и nontemplates: a) это действительно? б) не игнорируются ли теги в наборе перегрузки? Если пользователь вынужден писать "p- > template f < > (), то ясно, что это действительно так, и одинаково ясно, что nontemplates в наборе перегрузки игнорируются. Поскольку эта функция была добавлена исключительно для обеспечения синтаксического руководства, я думаю, что важно, чтобы в противном случае они не имели семантических последствий.
По сути, очень тонкое изменение DR228 было утрачено во время последующей ревизии; однако, поскольку никакого подобного ограничения не было установлено, намерение DR228, вероятно, все еще сохраняется, если в стандарте не было другого пересмотра. Это означает, что поиск шаблона должен происходить глобально в этом случае, даже если он является зависимым типом.
Посмотрите на наши правила поиска имен. §3.4.5.1:
В выражении доступа члена класса (5.2.5), если токен
.
или->
сразу же следует идентификатором, за которым следует<
, идентификатор должен быть просмотрен, чтобы определить, будет ли<
- это начало списка аргументов шаблона (14.2) или оператора меньше. Идентификатор сначала просматривается в классе выражения объекта. Если идентификатор не найден, он затем просматривается в контексте всего постфиксного выражения и называет шаблон класса.
Это явно указывает, что в baz.foo->template bar<T>();
Мы сначала рассмотрим контекст класса, включая стандартный поиск шаблона. После этого и если ничего не найдено, если форма выражения верна, мы переходим к контексту всего выражения. По существу, это продвигалось, и поиск этого имени должен выполняться таким же образом, если строка просто читается template bar<T>();
Действительно, хотя мы уже знали об этом с DR228. Я просто хотел дважды проверить и подтвердить. Реальный вопрос заключается в том, какой шаблон должен получить приоритет, тот, который находится в глобальной области видимости или в области видимости класса.
Для этого нам теперь нужно спросить о неквалифицированном поиске имени, потому что теперь бар рассматривается в том же контексте, что и foo, поэтому он больше не следит за правилами поиска членов, он следит за нормальными, неквалифицированными правилами поиска шаблонов, которые, естественно, предпочитают локальная версия.
Таким образом, в суммировании кажется, что Clang и MSVC демонстрируют правильное поведение, а GCC и EDG в этом случае не используются.
Мое лучшее предположение о том, почему GCC ошибается, выбирает неправильный контекст для назначения выражению после запуска правила. Вместо того чтобы помещать контекст на том же уровне, что и постфиксное выражение, оно может просто помещать его на глобальном уровне в случае аварии? Может быть, он просто пропускает первый шаг поиска? (Но это всего лишь предположение, я должен был бы выяснить, где искать в источнике GCC, чтобы сказать.) Это также объясняет, почему решение @Mikael Persson по изменению поиска к квалифицированному привело к тому, что компиляция началась снова.
В связанном отчете об ошибке от респондента есть люди, которые говорят о том, почему глобальная область видимости должна быть рассмотрена, и это должно быть, но, кажется, довольно прямолинейно, что локальное совпадение по объему должно иметь более высокий приоритет, чем глобальный. Похоже, в последнее время там немного активности.