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

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

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

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

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

Ответ 1

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

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

namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

Дополнительный шаг с использованием пустого тела важен, поэтому вы уже можете ссылаться в теле пространства имен на идентификаторы типа ::name, которые определены в этом пространстве имен, поскольку уже использовалась директива using.

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

namespace { int a1; }
static int a2;

Оба a являются единицей перевода локально и не будут сталкиваться при времени соединения. Но разница в том, что a1 в анонимном пространстве имен just получает уникальное имя. Он имеет внешнюю связь и может быть экспортирован в таблицу символов создаваемого объектного файла. Это становится важным, если вы хотите использовать его адрес в качестве аргумента шаблона:

template<int * ptr> struct sample { };

// OK - a1 has external linkage
sample<&a1> s1; 
// NOT OK - translation unit locality is done by giving a2 internal linkage. 
sample<&a2> s2; 

Параметры шаблона должны иметь внешнюю связь, поэтому в этом случае идентификатор должен быть помещен в анонимное пространство имен.

Прочитайте отличную статью на atau-computing ` Почему вместо статического? вместо имени не используется именное пространство имен.

Ответ 2

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

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

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

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

Ответ 3

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

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

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

    bool getState(userType*,otherUserType*);
}

И это на самом деле пессимизация: getState() имеет внешнюю связь. Обычно лучше предпочитать статическую связь, так как это не загрязняет таблицу символов. Лучше писать

static bool getState(/*...*/);

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

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