Механизм function-to-pointer conversions и отличие имени функции func от &func?

Рейтинг: 2Ответов: 1Опубликовано: 30.01.2023

В голове полная каша в этих вопросах, проясните пожалуйста все по полочкам..

Почему это утверждение:

void func(){};
static_assert(is_same<void(*)(), decltype(func)>::value); 

дает ошибку? Почему func тут не распадается до указателя на себя? Если я правильно понимаю, funс будет тем же самым что и &funс, но разница в том, что &funс сразу возвращает указатель на функцию, а funс (нативно) возвращает тип функции (или ссылку?), но автоматически преобразуется к указателю на функцию и становится тем же, что и &funс, верно? А в decltype этого преобразования (function-to-pointer conversions) не происходит, правильно?

В общем, когда мы используем имя функции в качестве выражения, оно распадается до указателя на само себя, но что это означает - что создается отдельная переменная-указатель, которая содержит адрес функции? Но что за адрес она может содержать, если функция это не объект?

А &func возвращает сразу указатель на функцию без каких-либо дополнительных преобразований? И опять-таки, что в этом контексте значит указатель - отдельная переменная в памяти? Или адрес в памяти? Но почему тогда называется указатель?

Ответы

▲ 6Принят

Ссылок на функции в вашем примере нет. Вопреки распространенному заблуждению, выражения не бывают ссылками. Ссылки всегда объявляются, например так: auto &x = func;.

Здесь появляется разница между типом выражения x и типом переменной x. Тип переменной - ссылка, но единственное место, где он может всплыть - это в decltype(x). В остальных местах важен только тип как выражения, и он уже не ссылка, а просто функция. Например в x(); или bar(x);.

когда мы используем имя функции в качестве выражения, оно распадается до указателя на само себя

Не при любом использовании. Например, здесь не распадается:

template <typename F>
void bar(F &) {}

bar(func);

распадается до указателя на само себя, но что это означает - что создается отдельная переменная-указатель, которая содержит адрес функции?

Да, но только объект, а не переменная. Переменные всегда объявляются (ссылки - подвид переменных), например auto &x = func; или auto *x = func; (можно без звездочки - это то же самое).

Все переменные (кроме ссылок) - это объекты, но не все объекты - переменные.

(Хотя, даже с тем, что это объект, можно поспорить - читайте про mandatory copy elision. Поскольку &func (или результат распада func до указателя) - это prvalue, сам объект появляется не сразу, а когда становится нужен. Например, в auto *x = func; объект один - x, распад не порождает второй (временный) объект. Но здесь это все не важно, ведь раз это не класс (у которых бывают конструкторы и деструкторы), нельзя никак пронаблюдать, сколько объектов на самом деле создается.)

Но что за адрес она может содержать, если функция это не объект?

Адрес функции, очевидно. Следствие: адреса бывают не только у объектов, но и у функций тоже. Скорее всего, это адрес первой процессорной инструкции этой функции.

А &func возвращает сразу указатель на функцию без каких-либо дополнительных преобразований?

Да.

И опять-таки, что в этом контексте значит указатель - отдельная переменная в памяти? Или адрес в памяти? Но почему тогда называется указатель?

Не переменная, потому что не объявлен. Не объект, потому что prvalue. Просто prvalue-указатель на функцию.

Или адрес в памяти?

Адрес - это значение указателя, так же как число - это значение (например) intа.

Поэтому создается указатель, значением которого является адрес функции.