Чому використовують неназвані простори імен та які їх переваги?


242

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

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

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

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

Відповіді:


189

Неназвані простори імен - це утиліта, щоб зробити одиницю перекладу ідентифікатора локальною. Вони поводяться так, ніби ви вибрали унікальне ім’я для одиниці перекладу для простору імен:

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

Додатковий крок із використанням порожнього тіла є важливим, тому ви вже можете направити в тілі простору імен такі ідентифікатори ::name, які визначені в цьому просторі імен, оскільки директива використання вже мала місце.

Це означає, що у вас можуть бути вільні функції (наприклад), helpякі можуть існувати в декількох одиницях перекладу, і вони не зіткнуться під час посилання. Ефект майже ідентичний використанню staticключового слова, яке використовується в C, яке ви можете ввести в декларацію ідентифікаторів. Неназвані простори імен є чудовою альтернативою, здатною навіть зробити блок перекладу типу локальним.

namespace { int a1; }
static int a2;

Обидва aє перекладацькою одиницею локальної і не зіткнуться під час зв’язку. Але різниця полягає в тому, щоa1 в анонімному просторі імен виходить унікальне ім’я.

Прочитайте чудову статтю на comeau-comuting Чому замість статичного використовується неназваний простір імен? ( Дзеркало Archive.org ).


Ви пояснюєте відношення до static. Чи можете ви також порівняти __attribute__ ((visibility ("hidden")))?
фінз

74

Якщо у анонімному просторі імен щось є, це означає, що він локальний для цього блоку перекладу (.cpp-файл і все його включення), це означає, що якщо інший символ з таким самим іменем визначений в іншому місці, не буде порушення Правила одного визначення (ODR).

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

Всі анонімні простори імен в одному файлі трактуються як одне і те саме простір імен, і всі анонімні простори імен у різних файлах відрізняються. Анонімний простір імен є еквівалентом:

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

14

Неназваний простір імен обмежує доступ класу, змінної, функції та об'єктів до файлу, в якому він визначений. Функціонал простору імен без імені схожий на 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

Окрім інших відповідей на це питання, використання анонімного простору імен також може підвищити продуктивність. Оскільки символи в просторі імен не потребують зовнішніх зв'язків, компілятор вільніше здійснювати агресивну оптимізацію коду в просторі імен. Наприклад, функція, яка викликається кілька разів один раз у циклі, може бути вбудована без будь-якого впливу на розмір коду.

Наприклад, у моїй системі наступний код займає близько 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
Занадто добре, щоб бути правдою - я спробував цей сегмент на gcc 4-1-2, використовуючи оптимізацію O3, з-і без оператора простору імен: -> Отримав той же час (3сек, з -О3 і 4сек з -О3)
Тео

2
Цей код був навмисно складним, щоб спробувати переконати компілятора не вводити b та add_val в main. Оптимізація O3 використовує безліч вкладених даних незалежно від вартості кодування. Однак все ж є ймовірні функції, де O3 не вбудував би add_val. Ви можете спробувати зробити add_val складнішим або викликати його декілька разів від main за різних обставин.
xioxox

5
@Daniel: що мені не вистачає? як читали, ви сказали, що порівнюєте -O3з собою, то ви сказали, що 3 проти 4 секунд - це "той самий час". жоден з них не має сенсу. я підозрюю, що справжнє пояснення буде, але що це?
підкреслюй_d

@underscore_d У обох випадках у відповіді зазначено -O2, а не -O3. Різні рівні оптимізації можуть поводитися по-різному. Також різні версії компілятора можуть поводитися по-різному (відповідь може застаріти, тобто)
Пол Стеліан

1
@PaulStelian Я це знаю, але здається досить зрозумілим, що я відповідав не на відповідь xioxox, а скоріше на коментар Тео (хоч або його ім’я змінилося, або я якось змішався)
underscore_d

12

Приклад показує, що люди в проекті, до якого ви приєдналися, не розуміють анонімних просторів імен :)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

Їм не потрібно знаходитись в анонімному просторі імен, оскільки constоб'єкт вже має статичну зв'язок і тому не може конфліктувати з однойменними ідентифікаторами в іншому блоці перекладу.

    bool getState(userType*,otherUserType*);
}

І це насправді песимізація: getState()має зовнішні зв’язки. Зазвичай краще віддати перевагу статичному з'єднанню, оскільки це не забруднює таблицю символів. Краще писати

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

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


10
Оскільки в + + 11 просторах імен без імені є внутрішні зв’язки (розділ 3.5 у стандарті або en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces )
Еміль Врійдагс

11
"Технологічно не потрібно знаходитись в анонімному просторі імен". Технічно, впевнено - але все ж, не завадить розмістити їх в одному, як візуальне нагадування про їх семантику і зробити це (навіть більше) тривіальним, щоб видалити constнісс пізніше за бажанням. Сумніваюсь, це означає, що команда ОП нічого не розуміє! Крім того, біт про функції в анонімних просторах імен, що мають зовнішній зв'язок, неправильний в C ++ 11 і далі, як зазначалося. Наскільки я розумію, вони вирішили проблему аргументів шаблону, раніше потребуючи зовнішнього зв’язку, тому могли дозволити просторам імен без імені (здатним містити аргументи шаблону) внутрішні зв'язки.
підкреслюй_d

11

Анонімний простір імен робить вкладені змінні, функції, класи тощо доступними лише всередині цього файлу. У вашому прикладі це спосіб уникнути глобальних змінних. Немає часу виконання або компіляції різниці в продуктивності.

Немає такої переваги чи недоліку, окрім "я хочу, щоб ця змінна, функція, клас тощо були публічними чи приватними?"


2
Тут можуть бути відмінності в роботі - дивіться мою відповідь тут. Це дозволяє компілятору краще оптимізувати код.
xioxox

2
Ти правий; принаймні, наскільки це сьогодні C ++. Однак для C ++ 98 / C ++ 03 потрібні речі мають зовнішній зв'язок, щоб використовувати їх як аргументи шаблону. Оскільки речі в анонімних просторах імен доступні як аргументи шаблону, вони матимуть зовнішню зв'язок (принаймні в попередньому C ++ 11), навіть якщо б не було можливості посилатися на них поза файлом. Я думаю, що можливо, в цьому є якась здатність підробляти, тому що стандарт вимагає, щоб речі діяли так, ніби правила виконувались; і іноді це можливо зробити, не по-справжньому виконуючи правила.
Макс Лібберт
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.