#c #type-inference #constexpr #c 20 #auto
#c #вывод типа #constexpr #c 20 #авто
Вопрос:
Почему определение переменной с ключевым словом не несет значения выражения, используемого для ее инициализации? auto
constexpr
В качестве примера рассмотрим следующий код:
#include <string_view>
constexpr std::string_view f() { return "hello"; }
static constexpr std::string_view g() {
constexpr auto x = f(); // (*)
return x.substr(1, 3);
}
int foo() { return g().length(); }
С помощью GCC 10.2 и --std=c 20 -fsanitize=undefined -O3
это компилируется в:
foo():
mov eax, 3
ret
Но если мы удалим constexpr в строке (*), мы получим 27-строчную программу с кучей указателей, длинной строковой константой и т. Д.
Примечания:
- Я отметил этот вопрос C 20, но у меня нет оснований полагать, что это поведение отличается от C 11.
- Этот вопрос не о примере, а об общем поведении
auto
w.r.t.constexpr
ness. Пример просто показывает, что GCC не обрабатывает x так, какconstexpr
если бы мы явно не указывали на это.
Комментарии:
1.
auto
работает так же, как и шаблоны. Const верхнего уровня игнорируется.constexpr
никогда не выводится.2.
constexpr
не является частью системы типов, это не то, что можно вывести.3. выражение constexpr не (гарантированно) вычисляется во время компиляции вне контекста constexpr. Эффект вашего примера вызван оптимизацией компилятора, и он может не соответствовать уровню оптимизации по умолчанию. Проверьте этот пример .
4. @Barry: Но это «выводится», когда вы хотите использовать выражение в контексте constexpr. Так почему бы не перенести эту функцию на auto вместе с этим типом?
5. Если у вас есть
template <typename T> void foo(T)
и передать егоconst int
,T
будетint
, неconst int
так как const верхнего уровня удаляется из выведенного типа. То же самое происходит сauto
. Он удаляетconst
.
Ответ №1:
auto
предназначен для включения вывода типа, а не для замены «всего полезного, что вы бы набрали здесь». constexpr
не является частью типа выражения и, следовательно, игнорируется auto
(в отличие от const
and volatile
, которые являются частью типа выражения и выводятся).
Но если мы удалим constexpr в строке (*), мы получим 27-строчную программу с кучей указателей, длинной строковой константой и т. Д.
Это выбор для вашего компилятора. Он содержит 100% информации, необходимой для удаления этого кода. Тот факт, что это не так, не касается стандарта C .
Это проблема «качества реализации», а не проблема стандартизации. Если реализация не будет выполнять столько вашего кода во время компиляции, сколько вы хотите, вы можете пожаловаться им на это.
Помните: constexpr
это не должно быть оптимизацией во время выполнения как таковой. Он предназначен для того, чтобы позволить вам писать вещи, которые вы иначе не смогли бы написать. Как std::get<g()>(some_tuple)
или что угодно. Этот код должен выполняться во время компиляции, поскольку он используется в параметре шаблона.
Я не спрашиваю о каком-то глубоком выводе, только о случае, когда функция явно является constexpr .
Давайте на мгновение забудем, что auto
это для вывода типов и constexpr
не является частью системы типов. Давайте вместо этого сосредоточимся на том, что, если auto
предполагалось вывести constexpr
в любом случае. Итак, вы хотите auto
, чтобы только выводить constexpr
, если <expr>
это конкретно обозначенная функция constexpr
.
Итак, давайте посмотрим на некоторый код:
auto x = constexpr_func();
auto y = constexpr_func() 5;
auto z = constexpr_func() constexpr_func();
auto w = constexpr_func_2() constexpr_func_2();
Какая из этих переменных constexpr
? Если то, что вы хотите, это то, что у нас было, тогда x
было бы constexpr
, но y
не было. Лично я нахожу это удивительным и раздражающим.
Хуже того, если мы предполагаем constexpr_func()
, что возвращает an int
, то z
также нет constexpr
. Но если constexpr_func_2()
возвращает определяемый пользователем литеральный тип, который имеет a constexpr operator
, тогда w
было бы constexpr
.
Разве все это не очень странно? Поэтому я очень подозреваю, что это не то, чего вы действительно хотите.
Что вы действительно хотите, так это auto x = <expr>;
определить constexpr
, будет ли constexpr auto x = <expr>;
это допустимо.
Но на самом деле это возвращается к исходной точке. Если вы создаете переменную constexpr
, это должно означать, что вы хотите, чтобы она использовалась в месте, где constexpr
требуется быть некоторым процессом. Учитывая этот факт, вывод constexpr
не имеет смысла, потому что вам это нужно constexpr
, чтобы не получить ошибку компиляции.
Комментарии:
1. »
constexpr
не предназначено для оптимизации во время выполнения» это x1002. Я чувствую, что этот ответ в основном повторяет фактическую ситуацию, а не причину. Мне интересно, почему
auto
в определение также не включена информация о типовой системе? Почему компилятор не должен обрабатыватьсяauto x = constexpr_func();
как переменная constexpr? Я не спрашиваю о каком-то глубоком выводе, только о случае, когда функция явно является constexpr .3. @einpoklum Рассмотрите другое поведение, которое вы предлагаете
auto a = 0;
,int b = 0;
и сколько кода сломается, еслиa
внезапно появитсяconstexpr
здесь.4. @einpoklum: » Мне интересно, почему auto не переносит информацию о типовой системе в определение? » Вы спрашиваете, почему инструмент, предназначенный для вывода типов, не выводит вещи, которые не являются частью системы типов? Почему компилятор должен специально обрабатывать этот конкретный случай?
5. @einpoklum: Но он не «ведет себя по-другому»; он компилируется по-другому. Поведение программы не меняется. Единственная причина, по которой нужно заботиться о том, как он компилируется, — это производительность.