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

StackOverflow https://stackoverflow.com/questions/154469

  •  03-07-2019
  •  | 
  •  

Вопрос

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

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

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

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

Это было полезно?

Решение

Стандарт C++ гласит в разделе 7.3.1.1 Безымянные пространства имен, параграф 2:

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

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

Редактировать:

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

Безымянные пространства имен по-прежнему имеют то преимущество, что позволяют определять локальные типы единиц перевода.Пожалуйста, посмотри этот ТАК вопрос для более подробной информации.

Кредит идет на Майк Перси за то, что обратил на это мое внимание.

Другие советы

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

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

Есть один крайний случай, когда статика имеет неожиданный эффект (по крайней мере, для меня).Стандарт C++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);
}

Само по себе это, вероятно, не так уж важно, но оно подчеркивает, что для полностью совместимого компилятора C++ (т.один с поддержкой 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.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

namespace {
   static int flag;
}

Его не будет видно в файле сопоставления

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

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

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

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

#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 и выше выдают предупреждения для неиспользуемой функции в пространстве имен, а также предупреждение для неиспользуемой статической функции.

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top