Механизм function-to-pointer conversions и отличие имени функции func от &func?
В голове полная каша в этих вопросах, проясните пожалуйста все по полочкам..
Почему это утверждение:
void func(){};
static_assert(is_same<void(*)(), decltype(func)>::value);
дает ошибку? Почему func тут не распадается до указателя на себя? Если я правильно понимаю, funс будет тем же самым что и &funс, но разница в том, что &funс сразу возвращает указатель на функцию, а funс (нативно) возвращает тип функции (или ссылку?), но автоматически преобразуется к указателю на функцию и становится тем же, что и &funс, верно? А в decltype этого преобразования (function-to-pointer conversions) не происходит, правильно?
В общем, когда мы используем имя функции в качестве выражения, оно распадается до указателя на само себя, но что это означает - что создается отдельная переменная-указатель, которая содержит адрес функции? Но что за адрес она может содержать, если функция это не объект?
А &func возвращает сразу указатель на функцию без каких-либо дополнительных преобразований? И опять-таки, что в этом контексте значит указатель - отдельная переменная в памяти? Или адрес в памяти? Но почему тогда называется указатель?
Ответы (1 шт):
Ссылок на функции в вашем примере нет. Вопреки распространенному заблуждению, выражения не бывают ссылками. Ссылки всегда объявляются, например так: 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а.
Поэтому создается указатель, значением которого является адрес функции.