anonymous namespace
Раз мы затрагиваем тему static и линковки, я не могу не рассказать про эту фичу. Есть такая штука, как безымянные или анонимные пространства имен. Они из себя представляют примерно следующее:
Как же можно получить доступ к содержимому этого неймспейса, если у него нет имени?
На самом деле имя есть. Его генерирует компилятор. Вот во что преобразуется пример выше внутри компилятора:
Будем разбирать по порядку, потому что здесь все непросто.
Во-первых, все, что мы объявили во всех безымянных пространствах шарится между ними внутри одного и того же скоупа, собственно как и для обычных нэймспейсов. Это благодаря тому, что все безымянные неймспейсы внутри одной единицы трансляции имеют одно и то же уникальное имя, генерируемое компилятором.
Во-вторых, все содержимое безымянных пространств видно из родительского пространства за счет дирекивы using. Благодаря этому мы можем пользоваться всеми членами unnamed namespace, как если бы они были в текущем неймспейсе.
В-третьих, на каждую единицу трансляции будет уникальное имя unique, которое больше никому не будет известно. Это значит, что ни одна другая единица трансляции не сможет получить доступ к intvar и foo, потому что не будет знать это уникальное имя.
И вот здесь ключевой момент. Хоть intvar и foo имеют внешнее связывание, но по сути к ним из другого юнита нельзя получить доступ. Значит они имеют эффект внутреннего связывания. Начиная вроде с 11-го стандарта там даже написано, что все члены безымянных неймспейсов имеют внутреннее связывание. Но это стандарт. Некоторые компиляторы типа VS2015/VS2017 считают все неконстантные переменные и свободные функции внутри безымянных пространств extern. На самом деле тут тонкий и не очень понятный момент, потому что именованные пространства имен содержат члены с внешним связыванием. А также в стандарте написано, что анонимное пространство раскрывается как именованое. Но теперь все объявления внутри почему-то имеют внутреннее связывание. Не эффект внутреннего связывания, а прям оно самое. Со всеми плюшками оптимизаций. Как это работает, мне не очень понятно. Знающие люди, отпишитесь в комментариях пожалуйста.
Что нужно понимать на верхнем уровне. Безымянные пространства имен используются для сокрытия данных от других единиц трансляции, обеспечивая видимость всего, что вы туда запихаете, только в текущей единице.
Если вы начали проводить параллели со static, то вы не ошибаетесь. static имеет тот же самый эффект на функции и на переменные. Он меняет связывание с внешнего на внутреннее. Безымянные неймспейсы делают то же самое, только на стероидах. Поэтому их и использовать можно в тех же сценариях и еще в некоторых других.
Следующий пост будет как раз про отличия unnamed namespace и static
Don't expose your secrets. Stay cool.
#cppcore #cpp11 #compiler
Раз мы затрагиваем тему static и линковки, я не могу не рассказать про эту фичу. Есть такая штука, как безымянные или анонимные пространства имен. Они из себя представляют примерно следующее:
namespace {
int int_var = 0;
void foo() {...}
}
Как же можно получить доступ к содержимому этого неймспейса, если у него нет имени?
На самом деле имя есть. Его генерирует компилятор. Вот во что преобразуется пример выше внутри компилятора:
namespace unique{}
using namespace unique;
namespace unique{
int int_var = 0;
void foo() {...}
}
Будем разбирать по порядку, потому что здесь все непросто.
Во-первых, все, что мы объявили во всех безымянных пространствах шарится между ними внутри одного и того же скоупа, собственно как и для обычных нэймспейсов. Это благодаря тому, что все безымянные неймспейсы внутри одной единицы трансляции имеют одно и то же уникальное имя, генерируемое компилятором.
Во-вторых, все содержимое безымянных пространств видно из родительского пространства за счет дирекивы using. Благодаря этому мы можем пользоваться всеми членами unnamed namespace, как если бы они были в текущем неймспейсе.
В-третьих, на каждую единицу трансляции будет уникальное имя unique, которое больше никому не будет известно. Это значит, что ни одна другая единица трансляции не сможет получить доступ к intvar и foo, потому что не будет знать это уникальное имя.
И вот здесь ключевой момент. Хоть intvar и foo имеют внешнее связывание, но по сути к ним из другого юнита нельзя получить доступ. Значит они имеют эффект внутреннего связывания. Начиная вроде с 11-го стандарта там даже написано, что все члены безымянных неймспейсов имеют внутреннее связывание. Но это стандарт. Некоторые компиляторы типа VS2015/VS2017 считают все неконстантные переменные и свободные функции внутри безымянных пространств extern. На самом деле тут тонкий и не очень понятный момент, потому что именованные пространства имен содержат члены с внешним связыванием. А также в стандарте написано, что анонимное пространство раскрывается как именованое. Но теперь все объявления внутри почему-то имеют внутреннее связывание. Не эффект внутреннего связывания, а прям оно самое. Со всеми плюшками оптимизаций. Как это работает, мне не очень понятно. Знающие люди, отпишитесь в комментариях пожалуйста.
Что нужно понимать на верхнем уровне. Безымянные пространства имен используются для сокрытия данных от других единиц трансляции, обеспечивая видимость всего, что вы туда запихаете, только в текущей единице.
Если вы начали проводить параллели со static, то вы не ошибаетесь. static имеет тот же самый эффект на функции и на переменные. Он меняет связывание с внешнего на внутреннее. Безымянные неймспейсы делают то же самое, только на стероидах. Поэтому их и использовать можно в тех же сценариях и еще в некоторых других.
Следующий пост будет как раз про отличия unnamed namespace и static
Don't expose your secrets. Stay cool.
#cppcore #cpp11 #compiler