Неоднозначная ссылка на переменную в C++

У меня есть этот фрагмент кода

#include <iostream>

namespace ns{
    int a = 10;
}

using namespace ns;
int a = 20;

int main(){
    std::cout << a;
}

Насколько я понимаю, когда std::cout << a оценивается, a ищется в локальной области main. Если он не найден, поиск выполняется в глобальной области, где находится a. Итак, я просто ожидал, что он напечатается 20, но код не компилируется, утверждая, что

error: reference to 'a' is ambiguous

Однако этот код работает:

#include <iostream>

namespace ns{
    int a = 10;
}

using namespace ns;
int a = 20;

int main(){
    std::cout << ::a;
}

Он печатает 20. Почему возникает эта разница? Разве эти два кода не должны быть эквивалентными, учитывая, что a не определен в области main, а поиск в обоих случаях выполняется в глобальной области?

Это связано с похожим вопросом, но я хочу конкретно понять разницу, вызванную заменой a на ::a, и то, что эти два слова не эквивалентны, а не просто неоднозначные ссылки на переменные.

Рассмотрим первый код. И подумайте, что произойдет, если программисты просто задумаются использовать ns::a. Так что это хорошая возможность — допускать явные программные ошибки, которые будет сложно найти. Мы не (современные) компьютеры/компиляторы, поэтому вам следует думать, что компьютерные языки также учитывают человеческий фактор (а не только простую семантику).

Giacomo Catenazzi 28.06.2024 11:00

оба a можно найти в глобальном пространстве имен.

463035818_is_not_an_ai 28.06.2024 11:07

@ 463035818_is_not_an_ai То же самое и во втором фрагменте кода, не так ли?

Rajdeep Sindhu 28.06.2024 11:14

@RajdeepSindhu да, но, как поясняет ответ, только в первом случае вы просите компилятор искать все a, которые он может найти. Во втором вы специально запрашиваете тот, который объявлен в глобальной области видимости.

463035818_is_not_an_ai 28.06.2024 11:16
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
110
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

using namespace влияет только на неполный поиск имени. Во втором примере вы явно ссылаетесь на ::a, символ в глобальной области видимости. Компилятор знает, что вы имеете в виду, все в порядке.
Но в первом случае вы ссылаетесь только на a без какого-либо квалификатора области, поэтому компилятор выполняет поиск, чтобы выяснить, какой a вы имеете в виду, и здесь возникают проблемы, поскольку и ::a, и импортированный ns::a видны.

Если using namespace влияет только на поиск неполных имен, то почему работает второй фрагмент кода, если мы удалим глобальный int a = 20;? На квалифицированный поиск a влияет директива using, не так ли?

Rajdeep Sindhu 29.06.2024 08:09
Ответ принят как подходящий

Взгляните на https://en.cppreference.com/w/cpp/language/namespace, особенно на

Директива using не добавляет никаких имен в декларативную область, в которой она появляется (в отличие от декларации using), и, таким образом, не предотвращает объявление идентичных имен.

Поскольку вы «всего лишь» используете using ns вместо using ns::a, вы не вводите a в глобальное пространство имен. Вы просто говорите компилятору искать в пространстве имен ns в дополнение к глобальному.

Таким образом, когда вы объявляете переменные a, проблем не возникает, поскольку одна из них находится в ns, а другая — в глобальном пространстве имен. Однако когда вы ищете неквалифицированный a, все члены ns также считаются частью глобального пространства имен (из-за using), поэтому теперь в одном пространстве имен есть два a.

Во втором примере ::a явно указывает использовать глобальное пространство имен; это больше не неквалифицированный поиск. По этому поводу https://en.cppreference.com/w/cpp/language/qualified_lookup есть следующее:

Квалифицированный поиск в области пространства имен N сначала рассматривает все объявления, расположенные в N... Если в этом наборе нет объявлений, то он рассматривает объявления во всех пространствах имен, названных директивами using, найденными в N...

Другими словами, «используемые» пространства имен рассматриваются ПОСЛЕ фактического пространства имен, поэтому двусмысленности больше нет.

Спасибо за ответ. Может быть, это немного не связано, но будет ли использование using namespace ns внутри main также сообщать компилятору о необходимости поиска ns в дополнение к глобальному пространству имен, или оно скажет компилятору искать ns в дополнение к локальному пространству имен main?

Rajdeep Sindhu 28.06.2024 11:39

В рамках main() использование using namespace ns в вопросе имеет тот же эффект, что и использование using namespace ns в main().

Weijun Zhou 28.06.2024 11:50

Другие вопросы по теме

Похожие вопросы

Есть ли причина, по которой сравнения 8-байтовых std::array создают разные сборки для char и std::byte?
Почему в двумерном массиве отображаются элементы, которые я не добавлял?
Android NDK: ошибка компиляции с std::execution в C++17
Можно ли определить, не имеет ли перечисление фиксированного базового типа?
Как настроить мои библиотеки и зависимости для компиляции моего приложения как статического
Принципы и практика программирования Страуструпа, 3-е издание, графика: почему передача функции обратного вызова в Window::timer_wait дает неожиданные результаты?
Почему я получаю ошибки «неопределенной ссылки» при попытке использовать OpenCV в Qt Creator с MinGW?
ESP-IDF: Модульные тесты с Unity и C++
Как передать constexpr в конструктор?
При поиске количества точек внутри некоторой фигуры с помощью KD-Tree нужно ли нам проверять пересечение областей или просто сравнивать свойства, соответствующие глубине?