Почему внутри неименованных пространств имен?

Быстрый для гуру: С++ 11 разрешает объявлять имена имен имен inline. Это кажется излишним для меня; вещи, объявленные в неназванном пространстве имен, уже используются так, как если бы они были объявлены в охватывающем пространстве имен.

Итак, мой вопрос: что значит сказать

inline namespace /*anonymous*/ {
    // stuff
}

и как он отличается от традиционного

namespace /*anonymous*/ {
    // stuff
}

что мы знаем и любим С++ 98? Может ли кто-нибудь привести пример другого поведения, когда используется inline?

РЕДАКТИРОВАТЬ: Просто пояснить, так как этот вопрос был отмечен как дубликат: я не спрашиваю об именах inline namespaces вообще. Я понимаю прецедент, и я думаю, что они здорово. Я специально спрашиваю, что значит объявлять неназванное пространство имен как inline. Поскольку неназванные пространства имен обязательно всегда являются локальными для TU, рациональное управление версиями символов, похоже, не применяется, поэтому мне интересно, что делает добавление inline.


В стороне, стандарт [7.3.1.1], касающийся неназванных пространств имен, говорит:

inline появляется в том и только в том случае, если он отображается в определении имен без имени

но это кажется тавтологией для моих неязычных глаз адвокатов - "оно появляется в определении, если оно появляется в определении"! Что касается бонусных очков, может ли кто-нибудь объяснить, что на самом деле говорит этот бит стандартного?

РЕДАКТИРОВАТЬ: Кубби заявил о бонусной точке в комментариях:

стандарт говорит о том, что определение имени-имени-имен ведет себя так, как если бы оно было заменено на X, где inline появляется в X, если оно появляется в пространстве имен без имени -определение

Ответы

Ответ 1

Вот одно использование, которое я нашел:

namespace widgets { inline namespace {

void foo();

} } // namespaces

void widgets::foo()
{
}

В этом примере foo имеет внутреннюю связь, и мы можем позже определить функцию, используя синтаксис namespace::function, чтобы убедиться, что подпись функции верна. Если бы вы не использовали пространство имен widgets, тогда определение void foo() определяло бы совершенно другую функцию. Вам также не нужно повторно открывать пространство имен, сохраняя уровень отступов.

Если в виджетах namespace уже есть еще одна функция, называемая foo, то это скорее приведет к двусмысленности, а не к неприятному нарушению ODR.

Ответ 2

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

Определение встроенного пространства имен включает в себя не только подъем имен в охватываемое пространство имен (что происходит в любом случае для неназванных пространств имен), но также позволяет настраивать шаблоны, определенные внутри встроенного пространства имен, вне его. Оказывается, это относится и к неназванным пространствам имен:

inline // comment this out to change behaviour
namespace { 
    template <typename T> struct A {};
}

template <> struct A<int> {};

Без inline g++ жалуется на попытку специализации шаблона из другого пространства имен (хотя Clang этого не делает). С inline он компилируется просто отлично. С обоими компиляторами все, что определено в специализации, по-прежнему отмечено как имеющее внутреннюю связь (согласно nm), как если бы оно находилось в неназванном пространстве имен, но я предполагаю, что это следует ожидать. Я не могу думать о какой-либо причине, почему это было бы полезно, но мы идем.

Вероятно, более полезный эффект исходит из изменения в отношении зависимого от аргументов поиска для встроенных пространств имен, что также влияет на неназванные внутренние пространства имен. Рассмотрим следующий случай:

namespace NS {
    // Pretend this is defined in some header file
    template <typename T>
    void func(const T&) {}

    // Some type definition private to this TU
    inline namespace {
        struct A {};
    }

} // end namespace NS

int main()
{
    NS::A a;
    func(a);
}

Без inline ADL терпит неудачу, и мы должны явно написать NS::func(a). Конечно, если бы мы определили неназванное пространство имен на уровне верхнего уровня (как обычно было бы), то мы бы не получили ADL, был ли он встроенным или нет, но все же...