anonymous namespace vs static



Вчера мы поговорили о том, что такое анонимные пространства имен. Эта фича обеспечивает внутреннее связывание всем сущностям, которые находятся в нем. Эффекты очень схожи с ключевым словом static, поэтому сегодня обсудим, какие между ними различия. Поехали!



1️⃣ static имеет очень много применений. Я бы сказал слишком много. Он и к функциям применим, и к переменным, и к методам, и к полям, и к локальным переменным. А еще он может бабушку через дорогу перевести и принять роды в ванной. Многофункциональный персонаж. Довольно сложно по-началу разобраться во всех тонкостях каждой стороны этой многогранной медали.



А вот unnamed namespace имеют одно применение - скрывают все свое содержимое от лишних глаз соседних юнитов трансляции. И все. Очень просто и понятно. И запомнить можно сразу.



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



2️⃣ В анонимных пространствах можно хранить все, что угодно! static хоть и применяется в куче ситуаций, он также не может быть применен в другой куче ситуаций. Например, к классам, енамам или даже другим неймспейсам. Если вы хотите полностью скрыть класс от внешних глаз, то его можно конечно определить внутри другого класса, но это не всегда подходящий вариант. А вот unnamed namespace с этим справляется очень хорошо. Это просто еще один дополнительный механизм, который позволит вам усилить безопасность кода и защитить от коллизий имен классов(нарушения ODR).



3️⃣ Не очень удобно каждую функцию, переменную или класс оборачивать в anonymous namespace, поэтому хотелось бы вынести все такие сущности в общий безымянный скоуп. Но тогда возникает проблема. При больших объемах кода внутри пространства начинаешь уже забывать, что смотришь на сущности с внутренним связыванием. Это заставляет больше информации держать в голове, что программисты делать очень не любят. Оперативка и так переполнена.



4️⃣ Вы не можете снаружи специализировать шаблон, объявленный внутри анонимного неймсейса. Об этом мы говорили в посте про inline namespace. Здесь такая же логика работает. И ADL тоже будет сложно.



5️⃣Был такой прикол, что некоторые шаблонные аргументы не могут быть внутренне связными сущностями. Помните, что шаблонный аргумент становится частью инстанцированного типа. Но сущности с внутренним связыванием не видны другим единицам трансляции, поэтому это дело не соберется.



Например



template <int const& Size>

class test {};



static int Size1 = 10;



namespace {

int Size2 = 10;

}



test<Size1> t1; // ERROR!!!

test<Size2> t2;




В этом примере не получится создать t1 по причинам описанным выше. А вот с t2 все хорошо, потому что Size2 имеет внешнее связывание(изначально внешнее, но из-за того, что никто не знает скрытого названия этого namespace'а, получается эффект внутреннего связывания). В прошлом посте мы об этом говорили. Почему я сказал, что был такой прикол? Начиная с С++17 мой gcc компилит этот пример полностью, поэтому проблему с невозможностью инстанцирования шаблонов с локально связными объектами пофиксили.



Я точно за использование anonymous namespace'ов при определении каких-то глобальных переменных. Они обычно компактные, их немного и все вместятся на экран внутри скоупа неймспейса. Это удобно читать и не надо везде приписывать static.



Также круто скрывать определения классов от ненужных глаз.



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



В целом, это все, что смог выдумать. Будут еще примеры или мысли - пишите, обсудим в комментах)



Use proper tools. Stay cool.



#cppcore #cpp17 #design