Безымянное / анонимное пространство имен против статических функций - PullRequest
468 голосов
/ 30 сентября 2008

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

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

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

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

Ответы [ 10 ]

308 голосов
/ 30 сентября 2008

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

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

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

Изменить:

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

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

Благодарность идет Майку Перси за то, что он привлек мое внимание.

65 голосов
/ 01 октября 2008

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

И, как указал luke, согласно стандарту, анонимные пространства имен предпочитаются статическим членам.

33 голосов
/ 01 октября 2008

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

Для вызова функции, который зависит от параметра шаблона, если имя функции является unqualified-id , но не template-id , функции-кандидаты находятся с использованием обычного правила поиска (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.

Обновление для Modern C ++

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

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

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

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

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

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

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

11 голосов
/ 01 октября 2008

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

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

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

7 голосов
/ 30 сентября 2008

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

6 голосов
/ 26 октября 2008

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

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

3 голосов
/ 08 декабря 2011

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

namespace {
   static int flag;
}

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

2 голосов
/ 18 апреля 2017

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

#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

2 голосов
/ 30 сентября 2008

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

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

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

0 голосов
/ 05 апреля 2018

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...