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


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

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

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

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


Answers


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

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

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

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

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

namespace { int a1; }
static int a2;

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

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. Почему вместо пространства имен используется неназванное пространство имен? ,




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

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

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

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;



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

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

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

    bool getState(userType*,otherUserType*);
}

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

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

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




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

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



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

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




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