Подтвердить что ты не робот

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

У меня есть функция, которая объявлена ​​и определена в файле заголовка. Это проблема сама по себе. Когда эта функция не включена, каждая единица перевода, которая использует этот заголовок, получает копию функции, и когда они связаны друг с другом, дублируются. Я "исправил" это, сделав функцию inline, но я боюсь, что это хрупкое решение, поскольку, насколько я знаю, компилятор не гарантирует вложения, даже если вы укажете ключевое слово "inline". Если это неверно, пожалуйста, исправьте меня.

В любом случае, реальный вопрос: что происходит со статическими переменными внутри этой функции? Сколько копий я получаю?

4b9b3361

Ответ 1

Я думаю, вы что-то упустили.

статическая функция?

Объявление функции static сделает ее "скрытой" в своем блоке компиляции.

Имя, имеющее область пространства имен (3.3.6), имеет внутреннюю связь, если это имя

- шаблон переменной, функции или функции, который явно объявлен статическим;

3.5/3 - С++ 14 (n3797)

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

3.5/2 - С++ 14 (n3797)

Если вы объявите эту статическую функцию в заголовке, то все единицы компиляции, включая этот заголовок, будут иметь свою собственную копию функции.

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

встроенная функция?

Объявление его inline делает его кандидатом на inlining (в настоящее время это не означает, что на С++ много, поскольку компилятор будет встроен или нет, иногда игнорируя факт присутствия или отсутствия ключевого слова inline):

Объявление функции (8.3.5, 9.3, 11.3) с встроенным спецификатором объявляет встроенную функцию. Спецификатор inline указывает на реализацию, что встроенная подстановка тела функции в точке вызова предпочтительнее обычного механизма вызова функции. Реализация не требуется для выполнения этой встроенной подстановки в точке вызова; однако, даже если эта встроенная подстановка опущена, другие правила для встроенных функций, определенных в 7.1.2, все равно должны соблюдаться.

7.1.2/2 - С++ 14 (n3797)

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

Для статических переменных, объявленных внутри, стандарт конкретно говорит там один и только один из них:

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

7.1.2/4 - С++ 98/С++ 14 (n3797)

(функции по умолчанию являются внешними, поэтому, если вы специально не отмечаете свою функцию как статическую, это относится к этой функции)

Это имеет преимущество "статический" (т.е. его можно определить в заголовке) без его недостатков (он существует не более одного раза, если он не встроен)

статическая локальная переменная?

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

static + inline?

Смешивание встроенных и статических будет иметь последствия, которые вы описали (даже если функция включена, статическая переменная внутри не будет, и вы закончите с такими же статическими переменными, как у вас есть единицы компиляции, включая определение ваши статические функции).

Отвечать автору на дополнительный вопрос

Так как я написал вопрос, я попробовал его с Visual Studio 2008. Я попытался включить все опции, которые заставляют VS действовать в соответствии со стандартами, но возможно, что я пропустил некоторые из них. Вот результаты:

Когда функция является просто "встроенной", существует только одна копия статической переменной.

Когда функция является "статической встроенной", существует столько копий, сколько единиц перевода.

Реальный вопрос заключается в том, должно ли это быть так, или если это идиосинкразия компилятора Microsoft С++.

Итак, у вас есть что-то вроде этого:

void doSomething()
{
   static int value ;
}

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

Вложение функции ничего не изменит:

inline void doSomething()
{
   static int value ;
}

Будет только одна скрытая глобальная переменная. Тот факт, что компилятор попытается встроить код, не изменит тот факт, что существует только одна глобальная скрытая переменная.

Теперь, если ваша функция объявлена ​​static:

static void doSomething()
{
   static int value ;
}

Тогда для каждой единицы компиляции это "private", что означает, что каждый файл CPP, включая заголовок, в котором объявлена ​​статическая функция, будет иметь свою собственную частную копию функции, включая свою собственную частную копию глобальной скрытой переменной, таким образом, как много переменных, поскольку есть единицы компиляции, включая заголовок.

Добавление "inline" к "статической" функции со "статической" переменной внутри:

inline static void doSomething()
{
   static int value ;
}

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

Итак, поведение VС++ верное, и вы принимаете реальный смысл "inline" и "static".

Ответ 2

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

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

Ответ 3

Я нашел ответ Mark Ransom полезным - что компилятор создает много копий статической переменной, но компоновщик выбирает один и применяет его ко всем единицам перевода.

В другом месте я нашел это:

См. [dcl.fct.spec]/4

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

У меня нет копии стандарта для проверки, но он соответствует моему опыту, рассматривая сборку в VS Express 2008

Ответ 4

Предполагается, что это так. "static" сообщает компилятору, что вы хотите, чтобы функция была локальной для блока компиляции, поэтому вы хотите, чтобы одна копия на единицу компиляции и одна копия статических переменных на экземпляр функции.

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

Примечание: этот ответ был написан в ответ на ответ оригинального плаката, опубликованного для себя.

Ответ 5

Так как я написал вопрос, я попробовал его с Visual Studio 2008. Я попытался включить все опции, которые заставляют VS действовать в соответствии со стандартами, но возможно, что я пропустил некоторые из них. Вот результаты:

Когда функция является просто "встроенной", существует только одна копия статической переменной.

Когда функция является "статической встроенной", существует столько копий, сколько единиц перевода.

Реальный вопрос заключается в том, должно ли это быть так, или если это идеосинхронизация компилятора Microsoft С++.

Ответ 6

Я полагаю, что у вас будет одна единица перевода. Фактически вы получили много версий этой функции (и ее объявленной статической переменной), по одной для каждой единицы перевода, содержащей заголовок.

Ответ 7

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

Ответ 8

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

Ответ 9

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