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

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

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

9 ответов


Я думаю, вы что-то упускаете, здесь.

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

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

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

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

3.5 / 3-C++14 (n3797)

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

3.5 / 2-C++14 (n3797)

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

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

inline функции?

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

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

7.1.2 / 2-C++14 (n3797)

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

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

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

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

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

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

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

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

статический + встроенный?

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

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

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

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

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

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

Итак, я полагаю, у вас есть что-то вроде что:

void doSomething()
{
   static int value ;
}

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

встраивание функции ничего не изменит:

inline void doSomething()
{
   static int value ;
}

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

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

static void doSomething()
{
   static int value ;
}

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

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

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

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

таким образом, поведение VC++ является правильным, и вы ошибаетесь в реальном значении "inline" и "static".


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

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


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

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

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

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

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


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

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

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


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

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

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

реальный вопрос заключается в том, являются ли вещи предполагается, что это так, или если это ideosyncracy компилятора Microsoft c++.


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


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


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


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