Ответ 1
Что касается С++ 03:
Абстрактная машина, определенная стандартом С++ 03, не содержит формального определения того, что такое поток, и каков результат программы, если к объекту обращаются одновременно.
Не существует понятия примитива синхронизации, упорядочения операций, выполняемых в разных потоках, расы данных и т.д. Поэтому, по определению, каждая многопоточная программа С++ 03 содержит поведение undefined.
Конечно, на практике реализации обеспечивают документированное поведение, но в стандарте нет ничего, что указывало бы, как это должно быть. Поэтому я бы сказал, что это зависит от вашего компилятора.
Остальная часть ответа будет сосредоточена на С++ 11, которая определяет семантику параллельных операций.
Что касается С++ 11:
Гарантируется ли, что
generateVar()
будет вызываться только один раз в любом сценарии (если используется, конечно)?
Нет, не в любом сценарии.
Инициализация var
гарантируется как потокобезопасная, поэтому generateVar()
не будет вводиться одновременно, но если исключение выбрано generateVar()
или конструктором копирования или конструктором перемещения SomeType
(если SomeType
- это UDT, конечно), то инициализация будет повторно предпринята в следующий раз, когда поток выполнения войдет в объявление, что означает, что generateVar()
снова будет вызван.
В параграфе 6.7/4 Стандарта С++ 11 при инициализации переменных области видимости блока со статической продолжительностью хранения:
[...] Если инициализация завершается путем исключения исключения, инициализация не является полным, поэтому он будет снова проверен в следующий раз, когда элемент управления войдет в объявление. Если контроль входит объявление одновременно, когда переменная инициализируется, одновременное выполнение должно ждать завершение инициализации. Если управление повторно вводит декларацию рекурсивно, пока переменная инициализировано, поведение undefined. [...]
Относительно вашего следующего вопроса:
Гарантировано ли, что foo вернет одно и то же значение при вызове несколько раз в любом сценарии?
Если ему удастся вернуть значение (см. выше), тогда да.
Есть ли разница в поведении для примитивных или не-примитивных типов?
Нет, нет, за исключением того, что нет такой вещи, как конструктор копирования или конструктор перемещения для примитивных типов, поэтому также нет риска, что инициализация копирования приведет к выбросу исключения (если, конечно, generateVar()
throws).