Я только что присоединился к новому программному проекту на C++ и пытаюсь разобраться в его конструкции. В проекте часто используются безымянные пространства имен. Например, что-то подобное может произойти в файле определения класса:
// newusertype.cc
namespace {
const int SIZE_OF_ARRAY_X;
const int SIZE_OF_ARRAY_Y;
bool getState(userType*,otherUserType*);
}
newusertype::newusertype(...) {...
Какие соображения дизайна могут привести к использованию безымянного пространства имен? Какие преимущества и недостатки?





Наличие чего-либо в анонимном пространстве имен означает, что оно является локальным для этого единица перевода (файла .cpp и всех его включений), это означает, что если другой символ с тем же именем определен в другом месте, не будет нарушения Одно правило определения (ODR).
Это то же самое, что и способ C иметь статическую глобальную переменную или статическую функцию, но он также может использоваться для определений классов (и должен использоваться, а не static в C++).
Все анонимные пространства имен в одном файле рассматриваются как одно и то же пространство имен, и все анонимные пространства имен в разных файлах различны. Анонимное пространство имен эквивалентно:
namespace __unique_compiler_generated_identifer0x42 {
...
}
using namespace __unique_compiler_generated_identifer0x42;
Анонимное пространство имен делает вложенные переменные, функции, классы и т. д. Доступными только внутри этого файла. В вашем примере это способ избежать глобальных переменных. Нет разницы в производительности во время выполнения или времени компиляции.
Нет особых преимуществ или недостатков, кроме «хочу ли я, чтобы эта переменная, функция, класс и т. д. Были общедоступными или частными?»
У вас есть пункт; по крайней мере, до сегодняшнего дня C++. Однако C++ 98 / C++ 03 требует, чтобы вещи имели внешнюю связь, чтобы их можно было использовать в качестве аргументов шаблона. Поскольку объекты в анонимных пространствах имен доступны как аргументы шаблона, они будут иметь внешнюю связь (по крайней мере, в версиях, предшествующих C++ 11), даже если не было возможности ссылаться на них извне файла. Я думаю, что здесь могла быть некоторая способность обмануть это, потому что стандарт требует только, чтобы все действовало так, как если бы правила были соблюдены; и иногда это можно сделать, не соблюдая правила.
Неназванные пространства имен - это утилита, позволяющая сделать локальную единицу преобразования идентификатора. Они ведут себя так, как если бы вы выбрали уникальное имя для каждой единицы перевода для пространства имен:
namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }
Дополнительный шаг с использованием пустого тела важен, поэтому вы уже можете ссылаться в теле пространства имен на идентификаторы, такие как ::name, которые определены в этом пространстве имен, поскольку директива using уже имела место.
Это означает, что у вас могут быть бесплатные функции, называемые (например) help, которые могут существовать в нескольких единицах перевода, и они не будут конфликтовать во время связывания. Эффект почти идентичен использованию ключевого слова static, используемого в C, которое вы можете вставить в объявление идентификаторов. Неназванные пространства имен являются превосходной альтернативой, поскольку они могут даже сделать локальную единицу преобразования типов.
namespace { int a1; }
static int a2;
Оба a являются локальными единицами перевода и не будут конфликтовать во время компоновки. Но разница в том, что a1 в анонимном пространстве имен получает уникальное имя.
Прочтите отличную статью на сайте comeau-computing Почему вместо статического используется безымянное пространство имен? (Зеркало Archive.org).
Вы объясните свое отношение к static. Не могли бы вы также сравнить с __attribute__ ((visibility ("hidden")))?
Пример показывает, что люди в проекте, к которому вы присоединились, не понимают анонимных пространств имен :)
namespace {
const int SIZE_OF_ARRAY_X;
const int SIZE_OF_ARRAY_Y;
Они не обязательно должны находиться в анонимном пространстве имен, поскольку объект const уже имеет статическую связь и, следовательно, не может конфликтовать с идентификаторами того же имени в другой единице перевода.
bool getState(userType*,otherUserType*);
}
И это на самом деле пессимизация: getState() имеет внешнюю связь. Обычно лучше предпочесть статическую привязку, так как это не загрязняет таблицу символов. Лучше написать
static bool getState(/*...*/);
здесь. Я попал в ту же ловушку (в стандарте есть формулировка, предполагающая, что статика файлов почему-то устарела в пользу анонимных пространств имен), но работая в большом проекте C++, таком как KDE, вы получаете множество людей, которые повернут вашу голову в правильном направлении. снова вокруг :)
Поскольку с ++ 11 безымянные пространства имен имеют внутреннюю связь (раздел 3.5 в стандарте или en.cppreference.com/w/cpp/language/namespace#Unnamed_namespa ces)
«Они не обязательно должны находиться в анонимном пространстве имен» Технически, конечно - но все же не помешает поместить их в одно, как визуальное напоминание об их семантике и сделать (даже более) тривиальным удаление constness позже при желании. Сомневаюсь, что это означает, что команда ОП «ничего не понимает»! Кроме того, как уже отмечалось, в C++ 11 и далее неверна информация о функциях в анонимных пространствах имен, имеющих внешнюю связь. Насколько я понимаю, они исправили проблему с аргументами шаблона, ранее требовавшими внешней связи, поэтому могли позволить безымянным пространствам имен (которые могут содержать аргументы шаблона) иметь внутреннюю связь.
Помимо других ответов на этот вопрос, использование анонимного пространства имен также может повысить производительность. Поскольку символы в пространстве имен не нуждаются в какой-либо внешней связи, компилятор свободнее выполнять агрессивную оптимизацию кода в пространстве имен. Например, функция, которая вызывается несколько раз в цикле, может быть встроена без какого-либо влияния на размер кода.
Например, в моей системе следующий код занимает около 70% времени выполнения, если используется анонимное пространство имен (x86-64 gcc-4.6.3 и -O2; обратите внимание, что дополнительный код в add_val заставляет компилятор не включать это дважды).
#include <iostream>
namespace {
double a;
void b(double x)
{
a -= x;
}
void add_val(double x)
{
a += x;
if (x==0.01) b(0);
if (x==0.02) b(0.6);
if (x==0.03) b(-0.1);
if (x==0.04) b(0.4);
}
}
int main()
{
a = 0;
for(int i=0; i<1000000000; ++i)
{
add_val(i*1e-10);
}
std::cout << a << '\n';
return 0;
}
Слишком хорошо, чтобы быть правдой - я пробовал этот сегмент на gcc 4-1-2, используя оптимизацию O3, с оператором пространства имен и без него: -> Получил то же время (3 секунды, с -O3 и 4 секунды с -O3)
Этот код был намеренно сложным, чтобы попытаться убедить компилятор не вставлять b и add_val в main. Оптимизация O3 использует много встраивания независимо от стоимости раздувания кода. Тем не менее, все еще существуют функции, в которых O3 не будет встраивать add_val. Вы можете попробовать сделать add_val более сложным или вызвать его несколько раз из main в разных обстоятельствах.
@ Дэниел: что мне не хватает? при чтении вы сказали, что сравнили -O3 с самим собой, а затем сказали, что 3 и 4 секунды - это «одно и то же время». ни то, ни другое не имеет смысла. Я подозреваю, что объяснение настоящий могло бы, но что это?
@underscore_d В ответе указано, что в обоих случаях использовалось -O2, а не -O3. Различные уровни оптимизации могут вести себя по-разному. Также разные версии компилятора могут вести себя по-разному (ответ может устареть, то есть)
@PaulStelian Я знаю это, но кажется довольно очевидным, что я отвечал не на ответ xioxox, а скорее на комментарий Тео (хотя либо его имя изменилось, либо я каким-то образом перепутался)
Безымянное пространство имен ограничивает доступ класса, переменной, функции и объектов к файлу, в котором оно определено. Функциональность безымянного пространства имен аналогична ключевому слову static в C / C++.
Ключевое слово static ограничивает доступ к глобальной переменной и функции к файлу, в котором они определены.
Существует разница между безымянным пространством имен и ключевым словом static, из-за которой безымянное пространство имен имеет преимущество перед статическим. Ключевое слово static может использоваться с переменной, функцией и объектами, но не с пользовательским классом.
Например:
static int x; // Correct
Но,
static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong
Но то же самое может быть возможно и с безымянным пространством имен. Например,
namespace {
class xyz {/*Body of class*/}
static structure {/*Body of structure*/}
} //Correct
Что такое static structure? Также почему он должен быть статическим?
Могут быть различия в производительности - см. Мой ответ здесь. Это позволяет компилятору лучше оптимизировать код.