У шаблонов переменных без constexpr нулевые накладные расходы?

Я имею в виду, что именно создается, когда вы создаете экземпляр переменной шаблона, отличной от constexpr?

Рассмотрим базовый шаблон переменной, который вычисляет факториал:

template<int N>
int fat = N*(fat<N-1>);

template<>
int fat<0> = 1;

int main() {
    return fat<5>;
}

Моя интуиция подсказывает, что это сгенерирует что-то вроде этого:

int fat0 = 1;
int fat1 = 1*fat0;
int fat2 = 2*fat1;
int fat3 = 3*fat2;
int fat4 = 4*fat3;
int fat5 = 5*fat4;

int main() {
    return fat5;
}

Я попытался взглянуть на это на C ++ Insights, но сгенерированный код выглядит так:

template<int N>
const int fat = N*(fat<N-1>);

template<>
const int fat<0> = 1;

int main()
{
  return fat<5>;
}

... что совсем не помогает.

Моя следующая попытка заключалась в том, чтобы взглянуть на (оптимизированную) сгенерированную сборку с помощью godbolt.org и посмотреть, есть ли какие-либо разница:

К моему удивлению, есть! В шаблонной версии примерно вдвое больше строк сборки, чем в рукописной. Кажется, что GCC генерирует дополнительную «охранную переменную» для каждого экземпляра. Clang также делает то же самое.

Теперь, учитывая принцип нулевых накладных расходов, эти переменные должны делать что-то важное. Конкретно то, что я упустил при написании своей "развернутой" версии. Что это за "что-то", что мне не хватает?

P.S .: Чтобы еще больше повредить моему мозгу, MSVC идет обратным путем, и сгенерированная сборка для шаблонной версии на самом деле в 3 раза меньше, чем версия без шаблонов. Однако я не могу понять смысла созданной сборки, поэтому я исключил ее из основного вопроса.


person Cássio Renan    schedule 18.08.2018    source источник


Ответы (1)


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

Но если вы объявите это static, оптимизатор сможет удалить дополнительные экземпляры:

template<int N>
static int fat = N*(fat<N-1>);

template<>
int fat<0> = 1;

int main() {
    return fat<5>;
}

NB: этот код можно использовать как пример того, почему не следует использовать Clang для C ++. Стек шаблонов clang испорчен, Clang возвращает 0. !!

Об ошибке Clang сообщается с 2016 г .: ошибка 29033

person Oliv    schedule 18.08.2018