Поиск зависимых имен в экземпляре шаблона C ++

Эльмар Зандер

Когда я пытаюсь скомпилировать этот код

// 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] Удалить.

Отредактировано в
0

я говорю два предложения

0обзор
Войти в системуУчаствуйте в комментариях

Статьи по теме

Что означает «статический» в C?

C ++ Цикл по объектам и указателям с одним и тем же кодом

Являются ли массивы в C синтаксическим сахаром для указателей?

Остановить цикл for, когда пользователь закончит ввод данных в c

неоднозначность имени функции c ++

Полиморфные ключи карты в C ++

C # - Как сделать следующий класс базовым, чтобы дочерние классы могли иметь одинаковые свойства, методы?

Почему строковый тип приводит к тому, что программа на c ++ не запускается?

Какие операторы в C имеют неправильный приоритет?

реализация списка типов в C ++

Заголовок стандартной библиотеки c ++ не распознается

функция-член передачи c ++

Прочтите строку из файла на C и извлеките номер ввода

Приведение аргументов универсального типа функции в c #

Встроенный код на C ++: параметры и их преимущества / недостатки

Когда мы должны использовать двумерный указатель в объекте c

ошибка: запрос на членскую "таблицу" не в структуре или объединении C

Определение и объявление функции C

C ++ преобразование из производного класса в базовый класс с другим номером шаблона

Чтение последнего документа в папке C #

c ++ - конструктор нельзя использовать для инициализации

грамматика c ++, тип возвращаемого значения в соответствии с этим кодом.

Ошибка приведения C # к дженерикам

возврат многомерного массива с явным типом возврата в c ++

Выражение указателей в C

Консоль C ++ - новые координаты широты и долготы с использованием азимута и диапазона

Альтернатива док-станции / привязки в C ++ для управления списком

Сравнение символов c ++

Агрегатная инициализация в C ++

TOP список

  1. 1

    Распределение Рэлея Curve_fit на Python

  2. 2

    Резервное копирование / восстановление kafka и zookeeper

  3. 3

    Редактировать существующий файл Excel C # npoi

  4. 4

    Ipython использует% store magic для получения динамического имени

  5. 5

    Как получить список индексов всех значений NaN в массиве numpy?

  6. 6

    Почему бы не выдать ошибку ERROR в тесте Jasmine?

  7. 7

    Дженерики и потоки Java

  8. 8

    Как отфильтровать несколько столбцов в Qtableview?

  9. 9

    Как добавить Swagger в веб-API с поддержкой OData, работающий на ASP.NET Core 3.1

  10. 10

    Как изменить значок приложения для проекта libgdx android

  11. 11

    Thymeleaf не отображает значения в проекте Spring Boot

  12. 12

    Unity Проверить, включен ли Toggle

  13. 13

    Airflow не распознает мои настройки подключения S3

  14. 14

    Flutter: Unhandled Exception: FileSystemException: Creation failed, path = 'Directory: '' (OS Error: Read-only file system, errno = 30)

  15. 15

    Bogue étrange datetime.utcnow()

  16. 16

    На графике Matplotlib не отображается легенда

  17. 17

    Создание X509Certificate2 из ECC X509Certificate выдает исключение System.NotSupportedException в C #

  18. 18

    Как добавить фреймворк в файл в папке «Источники» Xcode Playground?

  19. 19

    Выполнение команд PowerShell в программе Java

  20. 20

    Статус HTTP 403 - ожидаемый токен CSRF не найден

  21. 21

    Инструмент для вставки данных, собранных в электронной таблице, в документ Word или PDF

популярныйтег

файл