статические переменные во встроенной функции

<p>у меня есть функция, которая объявлена и определена в заголовочном файле. это проблема сама по себе. когда эта функция не встроена, каждый модуль перевода, который использует этот заголовок, получает копию функции, и когда они связаны друг с другом, они дублируются. я "исправил" это, сделав функцию встроенной, но я боюсь, что это хрупкое решение, потому что, насколько я знаю, компилятор не гарантирует встраивание, даже когда вы указываете ключевое слово "inline". если это не так, пожалуйста, поправьте меня.</p><p>в любом случае, реальный вопрос в том, что происходит со статическими переменными внутри этой функции? сколько копий я получу в итоге?</p>

<p>я полагаю, вы что-то упустили здесь.</p><h2>статическая функция?</h2><p>объявление статической функции сделает ее "скрытой" в модуле компиляции.</p><blockquote> <p>имя, имеющее область имен (3.3.6), имеет внутреннюю связь, если это имя</p> <p>- переменная, функция или шаблон функции, которые явно объявлены статическими;</p> <p>3,5/3 - с ++ 14 (n3797)</p> <p>когда имя имеет внутреннюю связь, обозначаемая им сущность может именоваться именами из других областей в той же единице перевода.</p> <p>3,5/2 - с ++ 14 (n3797)</p> </blockquote><p>если вы объявите эту статическую функцию в заголовке, то все модули компиляции, включая этот заголовок, будут иметь свою собственную копию функции.</p><p>дело в том, что если внутри этой функции есть статические переменные, у каждого модуля компиляции, включая этот заголовок, также будет своя собственная, персональная версия.</p><h2>встроенная функция?</h2><p>объявление inline делает его кандидатом для встраивания (в настоящее время это мало что значит для c++, так как компилятор будет встроенным или нет, иногда игнорируя тот факт, что ключевое слово inline присутствует или отсутствует):</p><blockquote> <p>объявление функции (8.3.5, 9.3, 11.3) со встроенным спецификатором объявляет встроенную функцию. встроенный спецификатор указывает реализации, что внутренняя замена тела функции в точке вызова должна быть предпочтительнее обычного механизма вызова функции. реализация не требуется для выполнения этой внутренней замены в точке вызова; однако, даже если эта встроенная замена не указана, другие правила для встроенных функций, определенные в 7.1.2, все равно должны соблюдаться.</p> <p>7.1.2/2 - c++ 14 (n3797)</p> </blockquote><p>в заголовке у него есть интересный побочный эффект: встроенная функция может быть определена несколько раз в одном и том же модуле, и компоновщик просто объединит "их" в один (если они не были встроены по причине компилятора).</p><p>для статических переменных, объявленных внутри, в стандарте конкретно указано одно и только одно из них:</p><blockquote> <p>статическая локальная переменная во внешней встроенной функции всегда ссылается на один и тот же объект.</p> <p>7.1.2/4 - c++ 98/c++ 14 (n3797)</p> </blockquote><p>(функции по умолчанию являются внешними, поэтому, если вы не пометите свою функцию как статическую, это относится к этой функции)</p><p>это имеет преимущество "статического" (то есть может быть определено в заголовке) без его недостатков (оно существует не более одного раза, если оно не встроено)</p><h2>статическая локальная переменная?</h2><p>статические локальные переменные не имеют связи (на них нельзя ссылаться по имени за пределами их области видимости), но они имеют статическую продолжительность хранения (т. е. они глобальные, но их создание и уничтожение подчиняются определенным правилам).</p><h2>статический + встроенный?</h2><p>смешивание inline и static будет иметь последствия, которые вы описали (даже если функция встроенная, статической переменной внутри не будет, и вы получите столько статических переменных, сколько у вас будет единиц компиляции, включая определение ваших статических функций. ).</p><h2>ответ на дополнительный вопрос автора</h2><blockquote> <p>с тех пор как я написал вопрос, я попробовал его в visual studio 2008. я попытался включить все параметры, которые заставляют vs работать в соответствии со стандартами, но, возможно, я пропустил некоторые из них. вот результаты:</p> <p>когда функция просто "встроенная", есть только одна копия статической переменной.</p> <p>когда функция "статическая встроенная", копий столько же, сколько единиц перевода.</p> <p>реальный вопрос сейчас заключается в том, должны ли вещи быть такими, или это особенность компилятора microsoft c++.</p> </blockquote><p>итак, я полагаю, у вас есть что-то вроде этого:</p><pre><code>void dosomething() { static int value ; } </code></pre><p>вы должны понимать, что статическая переменная внутри функции, проще говоря, глобальная переменная, скрытая для всех, кроме области действия функции, означая, что только функция, в которой она объявлена, может достичь ее.</p><p>вставка функции ничего не изменит:</p><pre><code>inline void dosomething() { static int value ; } </code></pre><p>там будет только одна скрытая глобальная переменная. тот факт, что компилятор попытается встроить код, не изменит тот факт, что существует только одна глобальная скрытая переменная.</p><p>теперь, если ваша функция объявлена статической:</p><pre><code>static void dosomething() { static int value ; } </code></pre><p>тогда он является "закрытым" для каждой единицы компиляции, что означает, что каждый файл cpp, включая заголовок, в котором объявлена статическая функция, будет иметь свою собственную частную копию функции, включая собственную частную копию глобальной скрытой переменной, таким образом, столько же переменных, сколько есть блоки компиляции, включая заголовок.</p><p>добавление inline к статической функции со статической переменной внутри:</p><pre><code>inline static void dosomething() { static int value ; } </code></pre><p>имеет тот же результат, что и не добавление этого встроенного ключевого слова, если говорить о статической переменной внутри.</p><p><b>таким образом, поведение vc++ правильное, и вы ошибаетесь в истинном значении слов "inline" и "static".</b></p>

<p>я считаю, что компилятор создает много копий переменной, но компоновщик выбирает одну и заставляет все остальные ссылаться на нее. у меня были похожие результаты, когда я пытался провести эксперимент по созданию разных версий встроенной функции; если функция не была встроенной (режим отладки), все вызовы переходили к одной и той же функции независимо от исходного файла, из которого они были вызваны.</p><p>подумайте на мгновение, как компилятор - как иначе? каждый модуль компиляции (исходный файл) не зависит от других и может быть скомпилирован отдельно; поэтому каждый из них должен создать копию переменной, думая, что она единственная. компоновщик имеет возможность выходить за эти границы и настраивать ссылки как для переменных, так и для функций.</p>

<p>мне был полезен ответ марка рэнсома: компилятор создает много копий статической переменной, но компоновщик выбирает одну и применяет ее во всех единицах перевода.</p><p>в другом месте я нашел это:</p><p><strong>см. [dcl.fct.spec]/4</strong></p><blockquote> <p>[..] встроенная функция с внешней связью должна иметь одинаковый адрес во всех единицах перевода. статическая локальная переменная во внешней встроенной функции всегда ссылается на один и тот же объект. строковый литерал во внешней встроенной функции - это один и тот же объект в разных единицах перевода.</p> </blockquote><p>у меня нет копии стандарта для проверки, но он соответствует моему опыту изучения сборки в vs express 2008</p>

<p>так и должно быть. "static" говорит компилятору, что вы хотите, чтобы функция была локальной для модуля компиляции, поэтому вы хотите одну копию на единицу компиляции и одну копию статических переменных на экземпляр функции.</p><p>"inline" используется для указания компилятору, что вы хотите, чтобы функция была встроенной; в настоящее время он просто воспринимает это как "все в порядке, если есть несколько копий кода, просто убедитесь, что это одна и та же функция". так что все разделяют статические переменные.</p><p>примечание: этот ответ был написан в ответ на ответ оригинального постера, размещенного для него.</p>

<p>с тех пор как я написал вопрос, я попробовал его в visual studio 2008. я попытался включить все параметры, которые заставляют vs работать в соответствии со стандартами, но, возможно, я пропустил некоторые из них. вот результаты:</p><p>когда функция просто "встроенная", есть только одна копия статической переменной.</p><p>когда функция "статическая встроенная", копий столько же, сколько единиц перевода.</p><p>реальный вопрос сейчас заключается в том, должны ли вещи быть такими, или это идеосинкратия компилятора microsoft c++.</p>