Почему `auto` не принимает constexpr своего инициализирующего выражения?

#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 не предназначено для оптимизации во время выполнения» это x100

2. Я чувствую, что этот ответ в основном повторяет фактическую ситуацию, а не причину. Мне интересно, почему auto в определение также не включена информация о типовой системе? Почему компилятор не должен обрабатываться auto x = constexpr_func(); как переменная constexpr? Я не спрашиваю о каком-то глубоком выводе, только о случае, когда функция явно является constexpr .

3. @einpoklum Рассмотрите другое поведение, которое вы предлагаете auto a = 0; , int b = 0; и сколько кода сломается, если a внезапно появится constexpr здесь.

4. @einpoklum: » Мне интересно, почему auto не переносит информацию о типовой системе в определение? » Вы спрашиваете, почему инструмент, предназначенный для вывода типов, не выводит вещи, которые не являются частью системы типов? Почему компилятор должен специально обрабатывать этот конкретный случай?

5. @einpoklum: Но он не «ведет себя по-другому»; он компилируется по-другому. Поведение программы не меняется. Единственная причина, по которой нужно заботиться о том, как он компилируется, — это производительность.