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

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

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

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

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

+431
источник поделиться
10 ответов

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

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

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

Edit:

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

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

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

+292
источник

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

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

+59
источник

Существует один краевой случай, когда статический эффект имеет удивительный аффект (по крайней мере, для меня). Стандарт С++ 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) найдены только объявления функций, найденные либо в контексте определения шаблона, либо в контексте экземпляра шаблона.

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

+31
источник

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

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

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

+10
источник

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

+7
источник

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

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

+6
источник

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

namespace {
   static int flag;
}

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

+3
источник

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

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

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

+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
источник

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

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

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

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

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

0
источник

Посмотрите другие вопросы по меткам или Задайте вопрос