Можно ли оптимизировать константу - PullRequest
1 голос
/ 09 июля 2020

Если у нас есть член класса static const, адрес которого никогда не используется, можно ли его оптимизировать и не выделять памяти?

Ответы [ 2 ]

3 голосов
/ 09 июля 2020

Ответ: это зависит от контекста .

Компилятор должен следовать правилу as-if, которое гарантирует, что программа должна вести себя так же оптимизированно, как и неоптимизировано - поэтому он может удалять только константы, которые, как он знает, не повлияют на поведение синтаксически допустимого кода.

Краткая версия

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

  • Константы тривиальны или имеют видимые конструкторы / деструкторы, которые не влияют на внешнее состояние ,
    • Этот эффект является транзитивным. Если конструктор / деструктор вызывает невидимую функцию, компилятор должен предположить, что этот вызов может изменить внешнее состояние и, следовательно, имеет значение.
  • Константы не используются ODR, и
  • Константы определены либо анонимно (в безымянном пространстве имен), либо определены встроенными

Длинная версия

Есть несколько случаев, которые влияют на резервное копирование хранилища

  1. Была ли константа была предоставлена ​​явная поддержка хранилища,
  2. Существует ли константа в безымянном пространстве имен,
  3. Имеет ли константа нетривиальное значение, невидимо конструкторы / деструкторы,
  4. Определена ли константа inline (C ++ 17),
  5. Определена ли константа inline (C ++ 17) и используется ODR,
  6. Определена ли константа в строке, но без поддержки хранилища

Давайте разберем эти разные разделы:

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 ++ есть острые углы, где вы можете ссылаться на частные члены в параметрах шаблона с помощью явных специализаций шаблона . Таким образом, даже если он логически не должен быть доступен, на самом деле это из представления языка (и, следовательно, компилятора).

1 голос
/ 09 июля 2020

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

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

...