Когда я пытаюсь скомпилировать этот код
// void foobar(int);
template <class T>
struct Foo {
void bar(T t) { foobar(t); };
};
void foobar(int);
template class Foo<int>;
с g ++ 4.8.2 я получаю следующее сообщение об ошибке
foo.cc: In instantiation of ‘void Foo<T>::bar(T) [with T = int]’:
foo.cc:10:16: required from here
foo.cc:5:27: error: ‘foobar’ was not declared in this scope, and no
declarations were found by argument-dependent lookup at
the point of instantiation [-fpermissive]
void bar(T t) { foobar(t); };
^
foo.cc:8:6: note: ‘void foobar(int)’ declared here, later in the translation unit
void foobar(int);
^
(С clang 3.4 почти то же самое).
Во-первых, я считаю, что код правильный и его следует компилировать, поскольку foobar является зависимым именем в объявлении шаблона и его следует искать только на втором этапе, когда создается экземпляр шаблона. Когда это делается в последней строке, foobar (int) уже объявлен. Код компилируется, BTW, когда я раскомментирую самую верхнюю строку, но оба объявления находятся до создания экземпляра, поэтому это не имеет значения.
Во-вторых, само сообщение об ошибке кажется мне противоречивым. В нем говорится, что «не было найдено объявлений в точке установки», то есть foo.cc:10:16, и говорится, что объявлено «позже» на foo.cc:8:6. Несмотря на то, что я знаю о числах и английском языке, я бы назвал это «до», а не «позже».
Итак, это ошибка в gcc или я что-то не так понял? Поскольку мне кажется, что это обычная схема использования, я не могу в это поверить.
Кстати: когда я пробую второй пример «Разрешения имен для зависимых типов» в MSDN ( http://msdn.microsoft.com/en-us/library/dx2zs2ee.aspx ) с g ++, результат отличается от vc ++, который (не обычно, но в этом конкретном случае) подорвало бы, что это ошибка в g ++.
tl; dr Foo<int>
не вызывает ADL, но Foo<X>
будет (где X
- тип класса).
Прежде всего, в этом коде foobar
есть зависимое имя из-за (C ++ 14 / N3936)[temp.dep]/1
В выражении формы:
postfix-expression ( expression-list opt )
где постфиксное-выражение - это неквалифицированный-идентификатор, неквалифицированный-идентификатор обозначает зависимое имя, если [...]
- любое из выражений в списке-выражений является выражением, зависящим от типа (14.6.2.2), или
и t
является зависимым именем, поскольку является частью объявления, T t
где T
- параметр шаблона и, следовательно, зависимый тип.
Переходя к зависимому разрешению имен, здесь [temp.dep.res]/1
вводится тот факт, что имена можно искать как в контексте определения, так и в контексте создания экземпляра, а также определяется, где находится контекст создания экземпляра. Я опустил это для краткости, но в этом примере template class Foo<int>;
это точка создания.
Следующий бит [temp.dep.candidate]/1
:
Для вызова функции, где постфиксное выражение является зависимым именем, функции-кандидаты находятся с использованием обычных правил поиска (3.4.1, 3.4.2), за исключением того, что:
- Для части поиска с использованием поиска по неквалифицированному имени (3.4.1) обнаруживаются только объявления функций из контекста определения шаблона.
- Для части поиска с использованием связанных пространств имен (3.4.2) обнаруживаются только объявления функций, найденные либо в контексте определения шаблона, либо в контексте создания экземпляра шаблона.
Эти последние две части представляют собой «две фазы» двухэтапного поиска. (Примечание: текст этого раздела изменился с C ++ 11 на C ++ 14, но эффект остался прежним).
На первом этапе, 3.4.1, имена для foobar
.
Итак, мы переходим ко второй фазе. Фактические места с названиями ищутся, как описано в 3.4.2. Текст длинный, но вот два важных правила:
Если T является фундаментальным типом, связанные с ним наборы пространств имен и классов пусты.
Если T является типом класса (включая объединения), то связанные с ним классы: сам класс; класс, членом которого он является, если есть; и его прямые и косвенные базовые классы. Связанные с ним пространства имен - это самые внутренние включающие пространства имен связанных с ним классов. [...]
Поэтому, когда вы создаете экземпляр Foo<int>
, вторая фаза поиска не вводит никаких дополнительных пространств имен для поиска.
Однако, если вы измените свой пример на наличие, struct X {};
а затем измените его int
на X
везде, код будет компилироваться. Это из-за последнего пункта маркера: ADL для аргумента типа класса выполняет поиск в охватывающем пространстве имен этого класса (который сейчас является глобальным пространством имен), однако ADL для аргумента встроенного типа не выполняет поиск в глобальном пространстве имен.
Эта статья взята из Интернета, укажите источник при перепечатке.
Если есть какие-либо нарушения, пожалуйста, свяжитесь с[email protected] Удалить.
я говорю два предложения