GCC делает это неправильно в соответствии с FCD. FCD говорит на 8.5.3
о привязке ссылки
- Если ссылка является ссылкой lvalue, а выражение инициализатора - [тип lvalue / class] ...
- В противном случае ссылка должна быть ссылкой lvalue на энергонезависимый тип const (т. Е. Cv1 должна быть const), или ссылка должна быть ссылкой rvalue, а выражение инициализатора должно быть значением rval или иметь тип функции.
Ваш случай вызова std::string &&
не соответствует ни одному из них, поскольку инициализатором является lvalue . Он не доходит до того, чтобы создать временное значение, потому что пуля верхнего уровня уже требует значения.
Теперь при разрешении перегрузки напрямую не используется привязка ссылок, чтобы увидеть, существует ли неявная последовательность преобразования. Вместо этого это говорит в 13.3.3.1.4/2
Когда параметр ссылочного типа не связан напрямую с выражением аргумента, последовательность преобразования является той, которая требуется для преобразования выражения аргумента в базовый тип ссылки в соответствии с 13.3.3.1.
Таким образом, разрешение перегрузки определяет победителя, даже если этот победитель фактически не сможет связать этот аргумент. Например:
struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };
void f(int&);
void f(B);
int main() { A a; f(a.bits); }
Привязка ссылок в 8.5
запрещает привязывать битовые поля к ссылкам lvalue. Но разрешение перегрузки говорит о том, что последовательность преобразования - это та, которая преобразуется в int
, и, таким образом, это происходит успешно, хотя, когда вызов выполняется позже, вызов неверно сформирован. Таким образом, мой пример битовых полей плохо сформирован. Если бы он выбрал версию B
, он бы был успешным, но требовал преобразования, определенного пользователем.
Однако , существуют два исключения для этого правила. Это
За исключением неявного параметра объекта, для которого см. 13.3.1, стандартная последовательность преобразования не может быть сформирована, если она требует привязки ссылки lvalue к non-const к rvalue или привязки ссылки rvalue к lvalue.
Таким образом, следующий вызов действителен:
struct B { B(int) { /* ... */ } };
struct A { int bits: 1; };
void f(int&); /* binding an lvalue ref to non-const to rvalue! */
void f(B);
int main() { A a; f(1); }
И, таким образом, ваш пример вызывает const T&
версию
void f(const std::string &);
void f(std::string &&); // would bind to lvalue!
void g(const char * arg) { f(arg); }
Однако, если вы скажете f(arg + 0)
, вы создадите значение r, и, следовательно, вторая функция будет жизнеспособной.