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

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

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

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

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

10.12.2008 20:03:05
6 ОТВЕТОВ
РЕШЕНИЕ

(Далее вещи - это вещи, которые больше не применяются к C ++ 11, но применимы к 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 зеркало ).

184
24.12.2019 06:05:12
Это действительно "все еще имеет внешнюю связь"? 3.5 / 4, кажется, предполагает, что все в анонимном пространстве имен получает внутреннюю связь ...?
Kerrek SB 28.08.2013 22:17:58
@kerr нет мой ответ устарел. Мне нужно будет обновить его для C ++ 11. Спасибо
Johannes Schaub - litb 29.08.2013 09:11:50
@ JohannesSchaub-litb: Спасибо. Я думаю, что мне придется пойти и удалить свой ответ по этой теме , так как это совершенно неправильно.
Kerrek SB 29.08.2013 09:13:00
@ JohannesSchaub-litb спасибо за попытку отделить то, что имеет отношение к делу, но этот ответ сейчас довольно сложно прочитать (для меня). Готовы ли вы вырезать зачеркнутые отрывки и оставить только соответствующий текст?
Razi Shaban 16.05.2017 00:03:54
В ответе на этот голос было много вычеркнутых утверждений, и это кажется им запутанным. Трудно понять смысл написанного для того, кто впервые видит пустое пространство имен (новичок в c ++).
IlliakaillI 1.02.2018 18:50:27

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

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

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

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;
68
1.03.2017 22:37:07

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

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

11
10.12.2008 21:19:28
Могут быть различия в производительности - смотрите мой ответ здесь. Это позволяет компилятору оптимизировать код лучше.
xioxox 29.08.2014 09:32:48
У вас есть пункт; по крайней мере, насколько C ++ сегодня. Однако C ++ 98 / C ++ 03 требует, чтобы вещи имели внешнюю связь для использования в качестве аргументов шаблона. Поскольку вещи в анонимных пространствах имен доступны в качестве аргументов шаблона, они будут иметь внешнюю связь (по крайней мере, до C ++ 11), даже если не было никакого способа обратиться к ним извне файла. Я думаю, что, возможно, была какая-то возможность обдумать это, потому что стандарт требует, чтобы все действовало так, как если бы правила применялись; и иногда это можно сделать без реального соблюдения правил.
Max Lybbert 29.08.2014 13:49:24

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

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

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

    bool getState(userType*,otherUserType*);
}

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

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

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

12
24.07.2009 17:20:04
Поскольку в безымянных пространствах имен c ++ 11 есть внутренняя связь (раздел 3.5 в стандарте или en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces )
Emile Vrijdags 6.04.2015 15:52:36
«Они не обязательно должны быть в анонимном пространстве имен». Технически, конечно, но все же не мешало бы поместить их в одно, как визуальное напоминание об их семантике и сделать ее (даже более) тривиальной, чтобы удалить constness позже при желании. Я сомневаюсь, что это означает, что команда ОП "ничего не понимает"! Кроме того, как было отмечено, в C ++ 11 и далее неправильно рассказывается о функциях в анонимных пространствах имен, имеющих внешнюю связь. Насколько я понимаю, они устранили проблему с аргументами шаблона, ранее нуждающимися во внешней связи, поэтому могли позволить безымянным пространствам имен (способным содержать аргументы шаблона) иметь внутреннюю связь.
underscore_d 10.07.2016 08:39:31

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

Например, в моей системе следующий код занимает около 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;
}
13
29.08.2014 09:34:53
Слишком хорошо, чтобы быть правдой - я попробовал этот сегмент на gcc 4-1-2, используя оптимизацию O3, с оператором пространства имен и без него: -> Получил одно и то же время (3 секунды, с -O3 и 4 секунды с -O3)
Theo 3.11.2015 23:08:20
Этот код был намеренно сложным, чтобы попытаться убедить компилятор не вставлять b и add_val в main. Оптимизация O3 использует много встраивания независимо от затрат на раздувание кода. Однако все еще существуют вероятные функции, в которых O3 не встроил бы add_val. Вы можете попытаться сделать add_val более сложным или вызвать его несколько раз из main в различных обстоятельствах.
xioxox 4.11.2015 08:29:30
@Daniel: что я пропускаю? как вы читали, вы сказали, что сравнили -O3с собой, затем вы сказали, что 3 против 4 секунд "то же самое время". ни один из них не имеет смысла. Я подозреваю, что реальное объяснение будет, но что это?
underscore_d 25.12.2015 02:13:24
@underscore_d В обоих случаях был указан ответ -O2, а не -O3. Различные уровни оптимизации могут вести себя по-разному. Кроме того, разные версии компилятора могут вести себя по-разному (ответ может быть устаревшим, то есть)
Paul Stelian 20.08.2018 11:57:24
@PaulStelian Я знаю это, но кажется довольно ясным, что я отвечал не на ответ xioxox, а на комментарий Тео (хотя либо его имя изменилось, либо я как-то перепутал)
underscore_d 29.08.2018 12:22:35

Безымянное пространство имен ограничивает доступ класса, переменной, функции и объектов к файлу, в котором он определен. Безымянная функциональность пространства имен похожа на 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
13
10.11.2017 00:21:40