Безымянное / анонимное пространство имен против статических функций

431

Особенностью С++ является возможность создания неназванных (анонимных) пространств имен, например:

namespace {
    int cannotAccessOutsideThisFile() { ... }
} // namespace

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

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

Теги:
namespaces

10 ответов

306
Лучший ответ

Стандарт С++ читается в разделе 7.3.1.1. Пространства имен, параграф 2:

Использование ключевого слова static устарел при объявлении объектов в область пространства имен, пространство имен без имени обеспечивает превосходную альтернативу.С >

Статичность применяется только к именам объектов, функций и анонимных объединений, а не к типу объявлений.

Edit:

Решение об отказе от использования ключевого слова static (влияет на видимость объявления переменной в блоке перевода) было отменено (ref), В этом случае использование статического или неназванного пространства имен вернулось к двум основным способам выполнения одной и той же вещи. Для более подробной информации см. этот вопрос SO.

Пространства имен по-прежнему имеют то преимущество, что вы можете определить типы единиц перевода. Подробнее см. .

Кредит переходит на Майк Перси, чтобы привлечь внимание к этому.

  • 34
    Head Geek спрашивает, какое статическое ключевое слово используется только для функций. Ключевое слово static, примененное к объекту, объявленному в области пространства имен, определяет его внутреннюю связь. Объект, объявленный в анонимном пространстве имен, имеет внешнюю связь (C ++ / 3.5), однако он гарантированно живет в области с уникальным именем. Эта анонимность безымянного пространства имен фактически скрывает его декларацию, делая его доступным только из модуля перевода. Последний эффективно работает так же, как статическое ключевое слово.
  • 5
    В чем недостаток внешней связи? Может ли это повлиять на встраивание?
Показать ещё 6 комментариев
59

Использование методов в анонимном пространстве имен предотвращает случайное нарушение правила One Definition Rule, позволяя вам никогда не беспокоиться о том, чтобы именовать ваши вспомогательные методы одинаково как другой метод, с которым вы можете связать.

И, как отметил Люк, анонимные пространства имен предпочтительнее стандартом над статическими членами.

  • 1
    Я имел в виду статические автономные функции (то есть функции с файловой областью), а не статические функции-члены. Статические автономные функции во многом аналогичны функциям в безымянном пространстве имен, поэтому возникает вопрос.
  • 2
    Ах; хорошо, ODR все еще применяется. Отредактировано для удаления абзаца.
Показать ещё 3 комментария
31

Существует один краевой случай, когда статический эффект имеет удивительный аффект (по крайней мере, для меня). Стандарт С++ 03 в 14.6.4.2/1:

Для вызова функции, который зависит от параметра шаблона, если имя функции является неквалифицированным идентификатором, но не идентификатором шаблона, функции-кандидаты определяются с использованием обычных правил поиска (3.4.1, 3.4.2), за исключением что:

  • Для части поиска, использующей поиск неквалифицированного имени (3.4.1), найдены только объявления функций с внешней связью из контекста определения шаблона.
  • Для части поиска с использованием связанных пространств имен (3.4.2) найдены только объявления функций с внешней связью, найденные либо в контексте определения шаблона, либо в контексте создания шаблона.

...

Ниже приведен код foo(void*), а не foo(S const &), как вы могли ожидать.

template <typename T>
int b1 (T const & t)
{
  foo(t);
}

namespace NS
{
  namespace
  {
    struct S
    {
    public:
      operator void * () const;
    };

    void foo (void*);
    static void foo (S const &);   // Not considered 14.6.4.2(b1)
  }

}

void b2()
{
  NS::S s;
  b1 (s);
}

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

// bar.h
export template <typename T>
int b1 (T const & t);

// bar.cc
#include "bar.h"
template <typename T>
int b1 (T const & t)
{
  foo(t);
}

// foo.cc
#include "bar.h"
namespace NS
{
  namespace
  {
    struct S
    {
    };

    void foo (S const & s);  // Will be found by different TU 'bar.cc'
  }
}

void b2()
{
  NS::S s;
  b1 (s);
}

Единственный способ гарантировать, что функция в нашем неназванном пространстве имен не будет найдена в шаблонах с использованием ADL, должна сделать ее static.

Обновление для современного С++

Как и в С++ '11, члены неназванного пространства имен непрямо связаны с внутренней связью (3.5/4):

Неименованное пространство имен или пространство имен, объявленное прямо или косвенно в неназванном пространстве имен, имеет внутреннюю привязку.

Но в то же время обновлен 14.6.4.2/1, чтобы удалить упоминание о связи (это взято из С++ '14):

Для вызова функции, где постфиксное выражение является зависимым именем, функции-кандидата найдены с использованием обычные правила поиска (3.4.1, 3.4.2), за исключением того, что:

  • Для части поиска, использующей поиск неквалифицированного имени (3.4.1), найдены только объявления функций из контекста определения шаблона.

  • Для части поиска с использованием связанных пространств имен (3.4.2) найдены только объявления функций, найденные либо в контексте определения шаблона, либо в контексте экземпляра шаблона.

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

  • 3
    Разве ключевое слово экспорта не должно быть мертвым? Единственные компиляторы, поддерживающие «экспорт», являются экспериментальными, и, если не удивительно, «экспорт» даже не будет реализован в других из-за непредвиденных побочных эффектов (в дополнение к тому, чего не ожидалось)
  • 2
    См. Статью Херба Саттера на тему: gotw.ca/publications/mill23-x.htm
Показать ещё 4 комментария
10

Недавно я начал заменять статические ключевые слова на анонимные пространства имен в моем коде, но сразу столкнулся с проблемой, когда переменные в пространстве имен больше не были доступны для проверки в моем отладчике. Я использовал VC60, поэтому я не знаю, является ли это проблемой, не связанной с другими отладчиками. Мое обходное решение состояло в том, чтобы определить пространство имен 'module', где я дал ему имя моего файла cpp.

Например, в моем файле XmlUtil.cpp я определяю пространство имен XmlUtil_I {...} для всех моих переменных и функций модуля. Таким образом, я могу применить XmlUtil_I:: квалификацию в отладчике для доступа к переменным. В этом случае "_I" отличает его от открытого пространства имен, такого как XmlUtil, которое я могу использовать в другом месте.

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

  • 7
    Я тоже это сделал, но с #if DEBUG namespace BlahBlah_private { #else namespace { #endif , поэтому «пространство имен модуля» присутствует только в отладочных сборках, а истинное анонимное пространство имен используется иначе. Было бы хорошо, если бы отладчики дали хороший способ справиться с этим. Doxygen смущается этим также.
  • 3
    безымянное пространство имен не является реальной заменой статического. static означает «на самом деле это никогда не будет связано с TU». Пространство имен без имени означает «оно все еще экспортируется как случайное имя, в случае, если оно вызывается из родительского класса, который находится вне TU» ...
7

Использование статического ключевого слова для этой цели устарело стандартом С++ 98. Проблема со статикой заключается в том, что она не применяется к определению типа. Это также перегруженное ключевое слово, используемое по-разному в разных контекстах, поэтому неназванные пространства имен немного упрощают вещи.

  • 1
    Если вы хотите использовать тип только в одном модуле перевода, объявите его в файле .cpp. В любом случае, он не будет доступен из других модулей перевода.
  • 4
    Вы бы подумали, не так ли? Но если другой модуль перевода (= cpp-файл) в том же приложении когда-либо объявит тип с тем же именем, у вас возникнут довольно трудные для отладки проблемы :-). Например, вы можете столкнуться с ситуациями, когда vtable для одного из типов используется при вызове методов для другого.
Показать ещё 1 комментарий
6

Из опыта я просто заметлю, что хотя С++-способ ставить ранее статические функции в анонимное пространство имен, у старых компиляторов иногда могут быть проблемы с этим. В настоящее время я работаю с несколькими компиляторами для наших целевых платформ, а более современный Linux-компилятор отлично подходит для размещения функций в анонимном пространстве имен.

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

3

Кроме того, если вы используете статическое ключевое слово для переменной, как в этом примере:

namespace {
   static int flag;
}

Он не будет отображаться в файле сопоставления

  • 5
    Тогда вам вообще не нужно анонимное пространство имен.
2

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

#include <iostream>

namespace
{
    void unreferenced()
    {
        std::cout << "Unreferenced";
    }

    void referenced()
    {
        std::cout << "Referenced";
    }
}

static void static_unreferenced()
{
    std::cout << "Unreferenced";
}

static void static_referenced()
{
    std::cout << "Referenced";
}

int main()
{
    referenced();
    static_referenced();
    return 0;
}

Компиляция этого кода с VS 2017 (указав флаг предупреждения уровня 4/W4 для включения предупреждение C4505: удаленная локальная функция удалена) и gcc 4.9 с помощью функции -Wunused-function или -Wall показывает, что VS 2017 будет выдавать предупреждение только для неиспользуемой статической функции. gcc 4.9 и выше, а также clang 3.3 и выше, будут выдавать предупреждения для функции unreferenced в пространстве имен, а также предупреждение для неиспользуемой статической функции.

Живая демонстрация gcc 4.9 и MSVC 2017

2

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

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

Мне было бы интересно узнать, использовал ли кто-то анонимные пространства имен в реальном коде.

  • 3
    Хорошие спекуляции, но неправильно. Область этих пространств имен распространяется на весь файл.
  • 0
    Не совсем верно, если вы определяете анонимное пространство имен внутри другого пространства имен, оно все еще имеет ширину файла и может рассматриваться только как находящееся в этом пространстве имен. Попытайся.
Показать ещё 1 комментарий
0

Лично я предпочитаю статические функции над безымянными пространствами имен по следующим причинам:

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

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

  • статические функции очень похожи на C или С++, а безымянные пространства имен - это, очевидно, только С++. безымянные пространства имен также добавляют дополнительный уровень, если отступ, и мне это не нравится:)

Итак, я рад видеть, что использование static для функций больше не устарело.

  • 0
    Предполагается, что функции в анонимных пространствах имен имеют внешнюю связь. Они просто изуродованы, чтобы сделать их уникальными. Только ключевое слово static самом деле применяет локальную связь с функцией. Кроме того, конечно, только бредовый сумасшедший фактически добавит отступ для пространств имен?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню