Понимание идеальной пересылки - PullRequest
0 голосов
/ 21 февраля 2019

Поскольку я понимаю, что rvalue, передаваемый в качестве аргумента в функцию, становится lvalue, std :: forward возвращает rvalue, если аргумент был передан как rvalue, и lvalue, если он был передан как lvalue.Вот мой класс:

#include <string>
#include <iostream>

struct MyClass
{
    MyClass()
    {
        std::cout << "default";
    }

    MyClass(const MyClass& copy)
    {
        std::cout << "copy";
    }

    MyClass& operator= (const MyClass& right)
    {
        std::cout << "=";
        return *this;
    }

    MyClass& operator= (const MyClass&& right)
    {
        std::cout << "mov =";
        return *this;
    }

    MyClass(MyClass&& mov)
    {
        std::cout << "mov constructor";
    }
};

void foo(MyClass s)
{
    MyClass z = MyClass(std::forward<MyClass>(s));
}

void main()
{
    auto a = MyClass();
    foo(MyClass()); //z is created by move_constructor
    foo(a); //z is created by move_constructor, but I think it must be created using copy constructor
}

Мой вопрос: почему переменная z создается с помощью move_constructor в обоих случаях.Я думал, что это должно быть перемещено в первом случае foo (MyClass ()) и скопировано во втором случае foo (a).Во втором случае я передаю lvalue в качестве аргумента s, и std :: forward должен возвращать lvalue, то есть затем передается как ссылка lvalue в конструктор MyClass.Где я не прав?

Ответы [ 2 ]

0 голосов
/ 21 февраля 2019

Я думаю, вы достаточно растеряны.Роль форварда только важна, когда в игру вступают универсальные ссылки, а универсальная ссылка - это что-то вроде T&& t, но только , когда T является параметром шаблона.

Например, в void foo(X&& x); x это не ссылка на пересылку, это обычная ссылка на значение, и пересылка не имеет смысла.Скорее, вы используете std::move, если хотите сохранить его rvaleness, в противном случае он становится l-значением:

void foo(X&& x) {
     bar(x); // calls bar with an l-value x, x should be not moved from

     baz(std::move(x)); // calls bar with an r-value x, x is likely moved from after this and probably unusable
}

Другими словами, вышеприведенная функция foo была специально создана для получения ссылок на rvalue, так как онааргумент, и больше ничего не приму.Вы, как писатель функций, определили его контракт таким образом.

Напротив, в контексте, подобном template <class T> void foo(T&& t) t, это ссылка на пересылку.Из-за правила свертывания ссылок это может быть ссылка rvalue или lvalue, в зависимости от значения выражения, переданного функции foo на сайте вызова.В таком случае вы используете

template<class T>
void foo(T&& t) {
    // bar is called with value matching the one at the call site                  
    bar(std::forward<T>(t));
}
0 голосов
/ 21 февраля 2019

Тип аргумента, который вы объявили - MyClass.Каким бы ни было выражение, которое инициализирует аргумент, не имеет значения в случае вашей функции - оно не влияет на тип аргумента.

MyClass не является ссылочным типом.std::forward преобразует выражение lvalue не ссылочного типа в значение rvalue.Использование std::forward в этом контексте эквивалентно std::move.

Обратите внимание, что сам аргумент создается при вызове foo(a).

.
...