Порядок использования пространства имен std; и включает?

недавно я видел, как этот код используется в исходном файле в проекте C++:

using namespace std;
#include <iostream>

игнорируя все вопросы о том, является ли это хорошей идеей иметь using namespace std вообще, является ли вышеуказанный код даже законным? В файле нет кода перед этими двумя строками.

Я бы подумал, что это не будет компилироваться, так как namespace std не было объявлено в области до #include <iostream> директива включает его в файл, но с помощью системы сборки для проекта это компилируется только штраф. Если у кого-то есть ссылка на соответствующую часть спецификации, это было бы наиболее оценено.

6 ответов


возможно, интересная точка данных. Когда я компилирую следующее:

using namespace std;
using namespace no_such_namespace;

С g++ 4.5.2 я получаю:

c.cpp:2:17: error: ‘no_such_namespace’ is not a namespace-name
c.cpp:2:34: error: expected namespace-name before ‘;’ token

ни std, ни no_such_namespace был определен как пространство имен в этот момент, но g++ жалуется только на второй. Я не думаю в идентификаторе есть что-то особенное std при отсутствии заявления об этом. Я думаю, что @James Kanze прав, что это ошибка в g++.

EDIT:и это доложили. (5 лет назад!)

UPDATE: теперь это больше, чем 8 лет, и до сих пор не было назначено никому, а тем более исправлено. g++ 4.9.2 демонстрирует проблему. clang++ 3.5 этого не делает, но он выдает предупреждение для std и фатальная ошибка для no_such_namespace:

c.cpp:1:17: warning: using directive refers to implicitly-defined namespace 'std'
using namespace std;
                ^
c.cpp:2:17: error: expected namespace name
using namespace no_such_namespace;
                ^
1 warning and 1 error generated.

Я не думаю, что это законно, но уровень не 100% понятно. В принципе, поиск имени (как определено в §3.4) не может найти предыдущий объявление пространства имен, потому что его нет. Всё зависит от того:

using namespace std;

является объявлением пространства имен или нет. И я не вижу никакого текста в §7.3.4 в котором говорится, что директива использования объявляет назначенный пространство имен. G++ позволяет ваш код, но IMHO, это ошибка.


из SO / IEC 14882: 2003

[7.3.3.9] сущность, объявленная декларацией использования, должна быть известна в контексте, использующем ее, в соответствии с ее определением в точке декларации использования. определения, добавленные в пространство имен после объявления using, не учитываются при использовании имени.

[3.4.3.2.2] учитывая X:: m (где X-объявленное пользователем пространство имен) или учитывая ::m( где X-глобальное пространство имен), пусть S - набор всех деклараций о м в X и в транзитивное замыкание всех пространств имен, выдвинутых с помощью директив X и ее использовать пространства имен, за исключением того, что с помощью директивы игнорируются в любом пространстве имен, включая X, непосредственно содержащие одну или несколько заявлений м. Имен не искали не раз в поиске имени. Если S-пустое множество, то программа плохо сформирована. В противном случае, если S имеет ровно один член, или если контекст ссылки является объявлением using - (7.3.3), S-необходимый набор объявлений m. В противном случае, если использование m не позволяет выбрать уникальное объявление из S, программа плохо сформирована

поэтому, если это сработает, это случайность, а не портативный.


этот код является неопределенным поведением [lib.с помощью.headers]:

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

ссылка std и затем включите заголовок, который объявляет его. Даже это все еще неопределенное поведение:

#include <string>
using namespace std;
#include <iostream>

Я думаю, что есть недостаток в стандарте (включая C++0x) в отношении этого случая.

мы имеем в разделе 3.3.6 ([basic.scope.namespace]):

декларативной областью определения пространства имен является его тело пространства имен. Масштаб обозначается оригинал-имен имя является объединение декларативного регионов, установленных в каждом из имен-определений в одной декларативной области с оригинальных имен-имя. Объекты, объявленные в namespace-body называются членами пространства имен, а имена, введенные этими объявлениями в декларативную область пространства имен, называются именами членов пространства имен. Имя члена пространства имен имеет область пространства имен. Его потенциальная область включает пространство имен от точки объявления имени (3.3.2) и далее; и для каждой директивы using-directive (7.3.4), которая назначает пространство имен члена, потенциальная область члена включает ту часть потенциальной области using-директива, которая следует за точкой объявления члена.

и

внешняя декларативная область единицы перевода также является пространством имен, называемым глобальным пространством имен. Имя, объявленное в глобальном пространстве имен, имеет глобальную область пространства имен (также называемую глобальной областью). Потенциальная область применения такого названия начинается в точке объявления (3.3.2) и заканчивается в конце единицы перевода, являющейся ее декларативной областью. Имена с мировой область пространства имен называется глобальным именем.

Так namespace std является членом глобального пространства имен и области имени начинается с пункт декларации.

и 3.3.2 ([basic.scope.pdecl]) говорит нам:

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

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

таким образом, имя пространства имен не может использоваться до его Декларатор, но имя пространства имен не Декларатор. Ой.


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