Может ли ссылка на rvalue связываться с функцией?

Я проверил следующий код с GCC, Clang, ICC и VS:

void f() {}

void g(void (&&)()) { }

int main() {
    g(f);
}

Как мы видим, g принимает ссылку rvalue, но f является lvalue и, вообще говоря, ссылки rvalue не могут быть привязаны к lvalues. Именно о том, что ICC жалуется о:

error: an rvalue reference cannot be bound to an lvalue

VS также дает ошибку, но по другой причине:

error C2664: 'void h(void (__cdecl &&)(void))' : cannot convert parameter 1 from 'void (__cdecl *)(void)' to 'void (__cdecl &&)(void)'

Это говорит мне, что VS немедленно выполняет преобразование от функции к указателю, а не напрямую связывает ссылку с f. Стоит отметить, что если я заменил g(f) на g(&f), то четыре компилятора произведут ту же самую ошибку.

Наконец, GCC и Clang принимают код, и я считаю, что они верны. Мои рассуждения основаны на 8.5.3/5

Ссылка на тип "cv1 T1" инициализируется выражением типа "cv2 T2" как

- Если ссылка является ссылкой lvalue [...]

- В противном случае [...] ссылка должна быть ссылкой rvalue.

    - Если выражение инициализатора является [...] функцией lvalue [...]

    то ссылка привязана к значению выражения инициализатора [...]

Является ли моя интерпретация правильной (то есть, Clang и GCC совместимы по данной причине)?

Ответ 1

Правильно ли моя интерпретация [...]?

Да.

Ваша интерпретация верна из-за параграфа Стандарта, который вы указали. Еще одно подтверждение вытекает из пункта 13.3.3.1.4/3 по ссылке:

За исключением неявного параметра объекта, для которого см. 13.3.1, стандартная последовательность преобразования не может быть если требуется привязать ссылку на значение lvalue, отличную от ссылки на нелетучий тип const, rvalue или привязка ссылки rvalue к lvalue, отличной от функции lvalue. [...]

В пункте 13.3.3.2/3 содержится дополнительное (косвенное) подтверждение:

[...] Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если

- [...]

- S1 и S2 являются привязками привязки (8.5.3), а S1 связывает ссылку lvalue с функцией lvalue , а S2 связывает ссылку rvalue с функцией lvalue. [Пример:

int f(void(&)()); // #1
int f(void(&&)()); // #2
void g();
int i1 = f(g); // calls #1

-end пример]