Ответ: это зависит от контекста .
Компилятор должен следовать правилу as-if, которое гарантирует, что программа должна вести себя так же оптимизированно, как и неоптимизировано - поэтому он может удалять только константы, которые, как он знает, не повлияют на поведение синтаксически допустимого кода.
Краткая версия
Краткая версия состоит в том, что константы могут быть оптимизированы, только если они соответствуют следующим критериям :
- Константы тривиальны или имеют видимые конструкторы / деструкторы, которые не влияют на внешнее состояние ,
- Этот эффект является транзитивным. Если конструктор / деструктор вызывает невидимую функцию, компилятор должен предположить, что этот вызов может изменить внешнее состояние и, следовательно, имеет значение.
- Константы не используются ODR, и
- Константы определены либо анонимно (в безымянном пространстве имен), либо определены встроенными
Длинная версия
Есть несколько случаев, которые влияют на резервное копирование хранилища
- Была ли константа была предоставлена явная поддержка хранилища,
- Существует ли константа в безымянном пространстве имен,
- Имеет ли константа нетривиальное значение, невидимо конструкторы / деструкторы,
- Определена ли константа
inline
(C ++ 17), - Определена ли константа
inline
(C ++ 17) и используется ODR, - Определена ли константа в строке, но без поддержки хранилища
Давайте разберем эти разные разделы:
1. Константе задается явная поддержка хранилища
Если вы определяете резервное копирование хранилища с самого начала, например:
struct example {
static const int value;
};
const int example::value = 5;
Тогда обычно будет резервное хранилище, поскольку компилятор должен предположить, что это будет ODR -используется в конечном итоге - даже если константа является частной.
Пример в обозревателе компилятора
2. Константа находится в безымянном пространстве имен
Однако, если эти типы определены в безымянном пространстве имен, что делает их анонимными символами для единицы перевода, то из-за отсутствия использования они могут быть оптимизированы:
namespace {
struct example {
static const int value;
};
const int example::value = 5;
}
Пример в обозревателе компилятора .
Это будет работать только для тривиальных конструкторов / деструкторов или конструкторов / деструкторов, которые компилятор в настоящее время может видеть для оптимизации инструкция.
3. Константа находится в безымянном пространстве имен с нетривиальным конструктором / деструктором
Если указанная выше константа имеет нетривиальный конструктор или деструктор, или конструктор / деструктор, который не виден, константы необходимо будет передать:
Либо:
namespace {
struct actor{
actor(); // not defined
~actor(); // not defined
};
class example {
static const actor value;
};
const actor example::value{};
}
Пример в обозревателе компилятора
или:
extern int some_global;
namespace {
struct actor{
actor(){ ::some_global = 5;}
~actor(){ ::some_global = 10;}
};
class example {
static const actor value;
};
const actor example::value{};
}
Пример в обозревателе компилятора
4. Константа определяется inline
(C ++ 17)
Если вы определяете значение inline static const
или inline static constexpr
в C ++ 17, то это зависит от того, используется ли оно ODR для того, используется ли символ будет когда-либо генерироваться.
Без использования ODR - Без эмиссии:
struct example {
inline static const int value = 5;
};
void test()
{
std::printf("%d", example::value);
}
Пример в Compiler Explorer .
5. Константа определяется inline
(C ++ 17) с использованием ODR
Если используется символ inline
, но используется ODR, необходимо создать резервную копию хранилища.
ODR использование - Эмиссия:
struct example {
inline static const int value = 5;
};
void consume(const int&);
void test()
{
consume(example::value)
}
Пример в обозревателе компилятора .
6. Константа определяется в строке, но без поддержки хранилища
В случае, если у вас есть встроенное определение объекта stati c const
(без inline
в C ++ 17), там никогда не должно быть резервной памяти. Константы могут быть созданы только с помощью константных выражений (с constexpr
или без него), но не имеют явно заявленной поддержки хранилища - это означает, что они не могут использоваться ODR, если не объявлены inline
.
Это что используется чертами типов C ++, поскольку они не создают никакого дополнительного кода, так как представляют собой объекты констант времени компиляции.
struct example {
static const int value = 5;
};
Пример в обозревателе компилятора
Изменить: Еще одно замечание, которое я хотел добавить: если для символа с не внутренней связью указана какая-либо поддержка хранения, либо через явные определения, либо через использование ODR с inline
символ, компилятор / компоновщик не должен быть в состоянии оптимизировать эту поддержку . По крайней мере, не из-за правильной интерпретации правила as-if (хотя некоторые нестандартные флаги оптимизации могут допускать это).
Проблема в том, что компилятор должен принять , что символы с внешняя связь все еще может быть названа или указана в другом месте, даже если это символ private
и никогда не доступен через friend
-ship. В C ++ есть острые углы, где вы можете ссылаться на частные члены в параметрах шаблона с помощью явных специализаций шаблона . Таким образом, даже если он логически не должен быть доступен, на самом деле это из представления языка (и, следовательно, компилятора).