Почему идеальная функция пересылки должна быть настроена? - PullRequest
18 голосов
/ 02 декабря 2011

Почему следующий код действителен:

template<typename T1>
void foo(T1 &&arg) { bar(std::forward<T1>(arg)); }

std::string str = "Hello World";
foo(str); // Valid even though str is an lvalue
foo(std::string("Hello World")); // Valid because literal is rvalue

Но не:

void foo(std::string &&arg) { bar(std::forward<std::string>(arg)); }

std::string str = "Hello World";
foo(str); // Invalid, str is not convertible to an rvalue
foo(std::string("Hello World")); // Valid

Почему lvalue в примере 2 не разрешается так же, как впример 1?

Кроме того, почему стандарт считает важным предоставить тип аргумента в std :: forward вместо простого его вывода?Простой вызов forward означает намерение независимо от типа.

Если это не стандартная вещь, а просто мой компилятор, я использую msvc10, что объясняет дрянную поддержку C ++ 11.

Спасибо

Редактировать 1: Литерал "Hello World" изменен на std :: string ("Hello World"), чтобы сделать его значением.

1 Ответ

15 голосов
/ 02 декабря 2011

Прежде всего, прочитайте это , чтобы получить полное представление о пересылке. (Да, я делегирую большую часть этого ответа в другом месте.)

Подводя итог, пересылка означает, что lvalues ​​остаются lvalues, а rvalues ​​остаются rvalues. Вы не можете сделать это с одним типом, поэтому вам нужно два. Таким образом, для каждого перенаправленного аргумента вам нужны две версии для этого аргумента, для которых требуется всего 2 N комбинаций для функции. Вы можете кодировать все комбинации функции, но если вы используете шаблоны, то эти различные комбинации генерируются для вас по мере необходимости.


Если вы пытаетесь оптимизировать копии и перемещения, например, в:

struct foo
{
    foo(const T& pX, const U& pY, const V& pZ) :
    x(pX),
    y(pY),
    z(pZ)
    {}

    foo(T&& pX, const U& pY, const V& pZ) :
    x(std::move(pX)),
    y(pY),
    z(pZ)
    {}

    // etc.? :(

    T x;
    U y;
    V z;
};

Тогда вам следует остановиться и сделать это так:

struct foo
{
    // these are either copy-constructed or move-constructed,
    // but after that they're all yours to move to wherever
    // (that is, either: copy->move, or move->move)
    foo(T pX, U pY, V pZ) :
    x(std::move(pX)),
    y(std::move(pY)),
    z(std::move(pZ))
    {}

    T x;
    U y;
    V z;
};

Вам нужен только один конструктор. Guideline : если вам нужна ваша собственная копия данных, сделайте эту копию в списке параметров; это позволяет принять решение о копировании или перемещении до вызывающей стороны и компилятора.

...