Неназванное пространство имен в именованном пространстве имен

Некоторый код, который мне просили изменить, выглядит примерно так:

namespace XXX {

namespace {

// some stuff

} // end of unnamed

// Some stuff within the scope of XXX

} // end of XXX

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

namespace {

// some stuff

} // end of unnamed

namespace XXX {

// Some stuff within the scope of XXX

} // end of XXX

Любые мнения будут с благодарностью оценены.

Ответ 1

Хорошо, получается, что X::<anonymous>::foo() отображается как X::foo(). Я удивлен.

Итак, нет, очень мало практических преимуществ. Однако могут быть семантические или документальные последствия.


Оригинальный ответ

Ну, это скорее зависит от "материала", не так ли?

Существующий код позволяет коду в X иметь "private" другой материал, который также находится в X, но не может быть доступен извне X:

#include <iostream>

namespace X {
   namespace {
      void foo() { std::cout << "lol\n"; }
   }

   void bar() { foo(); }
}

int main()
{
   X::bar();
   // X::foo();  // can't do this directly  [edit: turns out we can!]
}
  • Выход: lol\n

Ваш предлагаемый подход делает доступным для всего модуля перевода "частный материал":

#include <iostream>

namespace {
   void foo() { std::cout << "lol\n"; }
}

namespace X {
   void bar() { foo(); }
}

int main()
{
   X::bar();
   foo();     // works
}
  • Выход: lol\nlol\n

Ответ 2

Это имеет практическую пользу. Неименованное пространство имен скрывает имена внутри него от разных единиц перевода.

Вышеприведенный код работает только потому, что определение foo находится в той же самой системе перевода.

Предположим, что main() и определение foo() находятся в разных единицах перевода. Он будет скомпилирован, так как основной файл содержит заголовок объявления. но он не будет связываться, потому что логически нет такой вещи, как X::( неназванное пространство имен):: foo.

Ответ 3

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

С той же точки зрения единицы перевода есть разница. Тот факт, что вы определяете пространство имен toplevel, означает, что вы уменьшаете вероятность импорта конфликта пространства имен, объявленного в другом месте, а наиболее распространенным является глобальное namespace (namespaceless functions), думайте о чем-либо, унаследованном от ISO C, как от stdio.h или что-то еще).

Например, если глобальный заголовок, который вы импортируете в этой единицы перевода, имел "namespaceless" abort(), и вы объявляете пространство имен {abort() {...}} в своей единицы перевода, у вас была бы двусмысленность, gcc, например, выбрасывает ошибку компиляции:

error: call of overloaded ‘abort()’ is ambiguous

Теперь, если вы называете анонимное пространство имен внутри именованного пространства имен, у вас есть следующие эффекты:

a) нет никакой двусмысленности для функций, объявленных внутри пространства имен, поскольку имеет приоритет:

namespace a { namespace { abort() {...} } }

если у вас есть функция, подобная a:: whatever(), и она ссылается на abort(), она будет решать в своем собственном пространстве имен, поскольку она имеет приоритет.

b) У вас не будет глобальной привязки для a:: abort(), поскольку она не существует вне единицы перевода, так же, как пространство имен {abort(); } в верхнем уровне, но без потенциального конфликта выше.

И в "b" лежит разница: это не то же самое, что просто пространство имен a {abort(); }, потому что у него не будет глобальной привязки, поэтому вы можете переопределить его в другой единицы перевода без конфликтов. Удачи, пытаясь связать две единицы перевода, которые определяют пространство имен a {abort() {...}}...

Итак, вы получаете именно то, что вы подразумеваете:

namespace a { // you have a named space, so you don't have conflicts with the nameless one
  namespace { // but you have local visibility and linkage
    whatever(); // for this
  }
}

Короче говоря, оба пути имеют сходство, но есть разница. Можно утверждать, что это не очень полезно, но в качестве стиля он будет превентивно избегать столкновений с глобальным пространством имен. Можно все же утверждать, что, поскольку они будут пойманы во время компиляции (надеюсь, по крайней мере, когда подписи будут соответствовать идеально), зачем беспокоиться. Но это полезная концепция, если ваш проект представляет собой библиотеку, предназначенную для переносимости, и ваши заголовки могут быть загрязнены в зависимости от того, что импортируют сами заголовки среды, поскольку в противном случае вашим пользователям придется исправлять вашу библиотеку для своих систем или вам понадобится #ifdefs здесь и там.

Я много программирую по ISO/ANSI C 99, и от времени к разу я должен делать такие вещи, как:

#include <headerA.h>
#define symbol symbolB
#include <headerB.h>
// or some crap alike. And I have linker problems with above.

... потому что оба заголовка (например, из разных библиотек) могут загрязнять пространство имен, и я не могу просто поменять чужую библиотеку.

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