Почему используются безымянные пространства имен и каковы их преимущества?

Я только что присоединился к новому программному проекту на C++ и пытаюсь разобраться в его конструкции. В проекте часто используются безымянные пространства имен. Например, что-то подобное может произойти в файле определения класса:

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

Какие соображения дизайна могут привести к использованию безымянного пространства имен? Какие преимущества и недостатки?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
278
0
131 518
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

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

Это то же самое, что и способ C иметь статическую глобальную переменную или статическую функцию, но он также может использоваться для определений классов (и должен использоваться, а не static в C++).

Все анонимные пространства имен в одном файле рассматриваются как одно и то же пространство имен, и все анонимные пространства имен в разных файлах различны. Анонимное пространство имен эквивалентно:

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

Анонимное пространство имен делает вложенные переменные, функции, классы и т. д. Доступными только внутри этого файла. В вашем примере это способ избежать глобальных переменных. Нет разницы в производительности во время выполнения или времени компиляции.

Нет особых преимуществ или недостатков, кроме «хочу ли я, чтобы эта переменная, функция, класс и т. д. Были общедоступными или частными?»

Могут быть различия в производительности - см. Мой ответ здесь. Это позволяет компилятору лучше оптимизировать код.

xioxox 29.08.2014 13:32

У вас есть пункт; по крайней мере, до сегодняшнего дня C++. Однако C++ 98 / C++ 03 требует, чтобы вещи имели внешнюю связь, чтобы их можно было использовать в качестве аргументов шаблона. Поскольку объекты в анонимных пространствах имен доступны как аргументы шаблона, они будут иметь внешнюю связь (по крайней мере, в версиях, предшествующих C++ 11), даже если не было возможности ссылаться на них извне файла. Я думаю, что здесь могла быть некоторая способность обмануть это, потому что стандарт требует только, чтобы все действовало так, как если бы правила были соблюдены; и иногда это можно сделать, не соблюдая правила.

Max Lybbert 29.08.2014 17:49
Ответ принят как подходящий

Неназванные пространства имен - это утилита, позволяющая сделать локальную единицу преобразования идентификатора. Они ведут себя так, как если бы вы выбрали уникальное имя для каждой единицы перевода для пространства имен:

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")))?

phinz 22.03.2020 18:16

Пример показывает, что люди в проекте, к которому вы присоединились, не понимают анонимных пространств имен :)

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)

Emile Vrijdags 06.04.2015 18:52

«Они не обязательно должны находиться в анонимном пространстве имен» Технически, конечно - но все же не помешает поместить их в одно, как визуальное напоминание об их семантике и сделать (даже более) тривиальным удаление constness позже при желании. Сомневаюсь, что это означает, что команда ОП «ничего не понимает»! Кроме того, как уже отмечалось, в C++ 11 и далее неверна информация о функциях в анонимных пространствах имен, имеющих внешнюю связь. Насколько я понимаю, они исправили проблему с аргументами шаблона, ранее требовавшими внешней связи, поэтому могли позволить безымянным пространствам имен (которые могут содержать аргументы шаблона) иметь внутреннюю связь.

underscore_d 10.07.2016 11:39

Помимо других ответов на этот вопрос, использование анонимного пространства имен также может повысить производительность. Поскольку символы в пространстве имен не нуждаются в какой-либо внешней связи, компилятор свободнее выполнять агрессивную оптимизацию кода в пространстве имен. Например, функция, которая вызывается несколько раз в цикле, может быть встроена без какого-либо влияния на размер кода.

Например, в моей системе следующий код занимает около 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)

Theo 04.11.2015 02:08

Этот код был намеренно сложным, чтобы попытаться убедить компилятор не вставлять b и add_val в main. Оптимизация O3 использует много встраивания независимо от стоимости раздувания кода. Тем не менее, все еще существуют функции, в которых O3 не будет встраивать add_val. Вы можете попробовать сделать add_val более сложным или вызвать его несколько раз из main в разных обстоятельствах.

xioxox 04.11.2015 11:29

@ Дэниел: что мне не хватает? при чтении вы сказали, что сравнили -O3 с самим собой, а затем сказали, что 3 и 4 секунды - это «одно и то же время». ни то, ни другое не имеет смысла. Я подозреваю, что объяснение настоящий могло бы, но что это?

underscore_d 25.12.2015 05:13

@underscore_d В ответе указано, что в обоих случаях использовалось -O2, а не -O3. Различные уровни оптимизации могут вести себя по-разному. Также разные версии компилятора могут вести себя по-разному (ответ может устареть, то есть)

Paul Stelian 20.08.2018 14:57

@PaulStelian Я знаю это, но кажется довольно очевидным, что я отвечал не на ответ xioxox, а скорее на комментарий Тео (хотя либо его имя изменилось, либо я каким-то образом перепутался)

underscore_d 29.08.2018 15:22

Безымянное пространство имен ограничивает доступ класса, переменной, функции и объектов к файлу, в котором оно определено. Функциональность безымянного пространства имен аналогична ключевому слову 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? Также почему он должен быть статическим?

smac89 27.11.2020 08:27

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