Определение класса анонимного пространства имен

Я просматривал код (С++) и нашел что-то вроде этого:

//Foo.cpp
namespace
{
    void SomeHelperFunctionA() {}
    void SomeHelperFunctionB() {}
    void SomeHelperFunctionC() {}

    //etc...    

    class SomeClass //<---
    {
        //Impl
    };
}

SomeHelperFunction[A-Z] — это функции, которые нужны только в этой единице перевода, поэтому я понимаю, почему они находятся в анонимном namespace. Точно так же SomeClass также требуется только в этой единице перевода, но у меня сложилось впечатление, что вы можете иметь классы с одинаковыми именами в разных единицах перевода без каких-либо конфликтов имен при условии, что у вас нет объявления глобального класса (например, в обычно включаемом заголовочном файле).

Я также должен упомянуть, что эта конкретная единица перевода не включает какие-либо заголовки, которые могли бы объявлять класс с идентичным именем (SomeClass).

Итак, учитывая эту информацию, может ли кто-нибудь пролить свет на то, почему первоначальный программист мог это сделать? Может быть, просто в качестве меры предосторожности на будущее?

Честно говоря, я никогда раньше не видел, чтобы классы использовались в анонимных пространствах имен.

Спасибо!


person Community    schedule 18.07.2009    source источник


Ответы (5)


Анонимное пространство имен похоже на статическое ключевое слово, когда оно применяется на глобальном уровне.

Анонимное пространство имен делает так, что вы не можете вызывать что-либо внутри пространства имен из другого файла.

Анонимные пространства имен позволяют ограничить область действия только текущим файлом.

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

Пример:

Файл: test.cpp

namespace 
{
  void A()
  {
  }
  void B()
  {
  }
  void C()
  {
  }
}

void CallABC()
{ 
  A();
  B();
  C();
}

Файл: main.cpp

void CallABC();//You can use ABC from this file but not A, B and C

void A()
{
//Do something different
}

int main(int argc, char** argv)
{
  CallABC();
  A();//<--- calls the local file's A() not the other file. 
  return 0;
}

Вышеприведенное будет скомпилировано нормально. Но если вы попытаетесь написать функцию CallABC() в своем основном файле, у вас будет ошибка связывания.

Таким образом, вы не можете вызывать функции A(), B() и C() по отдельности, но вы можете вызвать CallABC(), который будет вызывать их все одну за другой.

Вы можете перенаправить объявление CallABC() внутри вашего main.cpp и вызвать его. Но вы не можете пересылать объявление test.cpp A(), B() или C() внутри вашего main.cpp, так как у вас будет ошибка связывания.

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

person Brian R. Bondy    schedule 18.07.2009
comment
Спасибо, Брайан, я понимаю, что анонимное пространство имен создает отдельное пространство имен с уникальным именем, которое предотвращает это. Но как эта внутренняя/внешняя связь применима к классам? Извините, если я не был ясен в своем вопросе. - person ; 18.07.2009
comment
Отмечен как отвеченный; больше усилий и более полное. ;) Спасибо. - person ; 18.07.2009
comment
Редактировать: функция test.cpp называется CallABC, тогда как весь остальной код ссылается на нее как на ABC — не могли бы вы обновить одну или другие? Спасибо! - person Kim Gräsman; 19.07.2009
comment
Ваше последнее предложение предполагает, что другие файлы могли бы использовать класс, если бы он не находился в анонимном пространстве имен. Но попробуйте скомпилировать его таким образом и посмотрите на таблицу символов объектного файла, например. с objdump -t. Свободные функции будут иметь открытые символы, а функции-члены класса — нет. У меня возникли проблемы с поиском ссылки на это в стандарте, но похоже, что класс действительно действует так, как будто он находится под внутренней связью, даже без анонимного пространства имен. - person Maxpm; 03.07.2018

Если кто-то связывает этот код и включает определение идентичного именованного класса, и этот файл связан перед другой реализацией, у вас будет конфликт имен.

person EricSchaefer    schedule 18.07.2009
comment
Что ж, теперь это имеет больше смысла. Спасибо! - person ; 18.07.2009
comment
Предполагая, что класс не определен в анонимном пространстве имен, то есть?? - person HelloGoodbye; 27.08.2013

В стандарте C++ ISO (раздел 2.3) вы найдете правило под названием Правило одного определения.

Из-за сложных взаимоотношений в C++ между компиляцией и компоновкой это не очень простое правило. Большая часть подробностей здесь.

Но это относится к функциям, которые являются членами класса, потому что они (на уровне компоновки) просто функции с длинными именами.

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

person Daniel Earwicker    schedule 18.07.2009

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

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

person MSalters    schedule 20.07.2009

Чтобы ответить на ваш вопрос напрямую, я думаю, что это сделано для того, чтобы избежать ошибок "множественного определения" компоновщика для членов static SomeClass. То есть, предположим, что SomeClass определен в cpp-файле без анонимного пространства имен, и в нем есть какие-то статические поля и/или статические методы, и эти статические члены определены рядом с определением класса в этом самом cpp-файле. Затем эти статические элементы получают внешнюю связь (присутствуют в таблице символов соответствующего файла .o как GLOBAL).

Теперь у вас есть еще один cpp-файл, и вы хотите создать еще один SomeClass (не связанный с тем, что находится в первом cpp-файле, и вы можете даже не знать о существовании первого SomeClass в этом первом cpp-файле) для ваших собственных целей и снова без анонимного пространства имен. И вы определяете статический член с тем же именем, что и статический член первого класса SomeClass. И вот вы: вы в конечном итоге с конфликтом связи.

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

Итак, в целом, я пытаюсь сказать, что не-constexpr/не-встроенные статические члены класса аналогичны глобальным переменным или не-встроенным функциям. Таким образом, их связь может быть сделана внутренней с использованием анонимного пространства имен точно так же, как это можно сделать для глобальных переменных и функций с помощью ключевого слова static или опять же анонимного пространства имен.

// .cpp file
namespace 
{
    struct A
    {
        static int i;
    };
}

int A::i;
person JenyaKh    schedule 24.02.2020