C++ 0x rvalue ссылки и временные файлы

(Я спросил изменение этого вопроса на comp.std.c ++, но не получил ответ.)

Почему делает вызов к f(arg) в этом коде называют константу касательно перегрузки f?

void f(const std::string &); //less efficient
void f(std::string &&); //more efficient

void g(const char * arg)
{
     f(arg);
}

Моя интуиция говорит что f(string &&) перегрузка должна быть выбрана, потому что arg потребности, которые будут преобразованы во временный файл независимо от того, что, и временные соответствия rvalue ссылка лучше, чем lvalue ссылка.

Это не то, что происходит в GCC и MSVC (редактирование: Спасибо Sumant: этого не происходит в GCC 4.3-4.5). В, по крайней мере, G ++ и MSVC, любой lvalue не связывает с rvalue ссылочным аргументом, даже если существует промежуточный созданный временный файл. Действительно, если константа касательно перегрузки не присутствует, компиляторы диагностируют ошибку. Однако запись f(arg + 0) или f(std::string(arg)) действительно выбирает rvalue ссылочную перегрузку, как Вы ожидали бы.

От моего чтения C++ 0x стандарт, кажется, что неявное преобразование символа константы * к строке нужно рассмотреть при рассмотрении если f(string &&) жизнеспособно, так же, как при передаче константы lvalue касательно аргументов. Разделите 13.3 (разрешение перегрузки) не дифференцируется между rvalue судьями и ссылками константы в слишком многих местах. Кроме того, кажется, что правило, которое препятствует тому, чтобы lvalues связал с rvalue ссылками (13.3.3.1.4/3), не должно применяться, если существует промежуточный временный файл - в конце концов, совершенно безопасно переместиться от временного файла.

Это:

  1. Меня неправильно читать/неправильно понимать стандарт, где реализованное поведение является намеченным поведением, и существует некоторое серьезное основание, почему мой пример должен вести себя способ, которым это делает?
  2. Ошибка, которую так или иначе все сделали поставщики компилятора? Или ошибка на основе общих стратегий реализации? Или ошибка в, например, GCC (где это lvalue/rvalue ссылочное правило привязки было сначала реализовано), который был скопирован другими поставщиками?
  3. Дефект в стандарте, или непреднамеренное последствие или что-то, что должно быть разъяснено?

Править: У меня есть последующий вопрос, который связан: C++ 0x rvalue ссылки - lvalues-rvalue привязка

14
задан Community 23 May 2017 в 11:48
поделиться

5 ответов

GCC делает это неправильно согласно FCD. FCD говорит в 8.5.3 о привязке ссылок

  • Если ссылка является ссылкой lvalue и выражение инициализатора является [lvalue / class type] ...
  • В противном случае ссылка должна быть ссылкой lvalue на энергонезависимый тип const (т.е. cv1 должен быть const), или ссылка должна быть ссылкой rvalue и выражение инициализатора должно быть rvalue или иметь тип функции.

Ваш случай с вызовом std::string && не соответствует ни одному из них, потому что инициализатор является lvalue. Он не доходит до места создания временного r-значения, потому что эта пуля верхнего уровня уже требует r-значения.

Теперь, разрешение перегрузки не использует непосредственно привязку ссылок, чтобы увидеть, существует ли неявная последовательность преобразования. Вместо этого в 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-значение, и таким образом вторая функция является жизнеспособной.

8
ответ дан 1 December 2019 в 13:47
поделиться

Я не заметил описанного Дугом поведения на g ++. g ++ 4.5 и 4.4.3 оба вызывают f (string &&) , как и ожидалось, но VS2010 вызывает f (const string &) . Какую версию g ++ вы используете?

2
ответ дан 1 December 2019 в 13:47
поделиться

Я не знаю, изменилось ли это в последних версиях стандарта, но раньше говорилось что-то вроде "если есть сомнения, не используйте ссылку rvalue ". Наверное, из соображений совместимости.

Если вам нужна семантика перемещения, используйте f (std :: move (arg)) , который работает с обоими компиляторами.

0
ответ дан 1 December 2019 в 13:47
поделиться

Многие вещи в текущем проекте стандарта нуждаются в разъяснении, если вы спросите меня. А компиляторы все еще развиваются, так что доверять их помощи сложно.

Кажется довольно очевидным, что ваша интуиция верна ... временные объекты любого типа должны связываться со ссылками на rvalue. Например, §3.10, новый раздел «таксономии», категорически определяет временные значения как rvalues.

Проблема может заключаться в том, что спецификации аргумента RR недостаточно для вызова создания временного объекта. §5.2.2 / 5: «Если параметр имеет ссылочный тип const, при необходимости вводится временный объект». Звучит подозрительно эксклюзивно.

Кажется, снова проскальзывает сквозь трещины в §13.3.3.1 / 6: (выделено мной)

Когда тип параметра не является ссылкой, неявная последовательность преобразования моделирует копирование-инициализацию параметра из выражение аргумента. Неявная последовательность преобразования - это последовательность, необходимая для преобразования выражения аргумента в значение типа параметра.

Обратите внимание, что строка инициализации копирования && rr = "hello"; отлично работает в GCC.

РЕДАКТИРОВАТЬ: На самом деле проблема не существует в моей версии GCC. Я все еще пытаюсь выяснить, как второе стандартное преобразование пользовательской последовательности преобразования связано с формированием ссылки rvalue. (Формирование RR - это вообще преобразование? Или оно продиктовано разрозненными лакомыми кусочками вроде 5.2.2 / 5?)

1
ответ дан 1 December 2019 в 13:47
поделиться

Взгляните на это:

http://blogs.msdn.com/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx

rvalue references: overload resolution

Похоже, что ваш случай таков: "Lvalues сильно предпочитают привязку к ссылкам lvalue".

0
ответ дан 1 December 2019 в 13:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: