по ссылке или по значению?
От: alexeiz  
Дата: 27.12.12 23:53
Оценка:
Думаю все уже читали вот это? Краткий смысл в том, что когда мы передаем параметр по значению в функцию, корорая должна сделать копию параметра, компилятор получает возможность соптимизировать конструктор копирования.

Т.е. сравним две фунции:
    void foo1(string par)
    {
        // modify par...
    }

    void foo2(string const & par)
    {
        string cpy(par);
        // modify cpy...
    }

То foo1 будет более оптимальна, чем foo2.

Однако в C++11 у нас есть возможность использовать &&, и мы перегрузим foo2 следующим образом:
    void foo2(string && par)
    {
        // modify par...
    }


Теперь foo2, перегруженная для const& и &&, будет немного более оптимальна, чем foo1. Но с другой стороны, если количество параметров, для которых нужно иметь подобные перегрузки, больше чем один, то количество перегрузок растет как 2^N. Зададимся таким вопросом: насколько имеет смысл делать несколько перегрузок с передачей параметров по ссылке вместо простой передачи параметров по значению?

Насколько вообще перегрузки для const& и && ссылок эффективнее передачи по значению?

Здесь надо рассмотреть три варианта: когда в функцию передается lvalue, xvalue и prvalue. prvalue — это pure rvalue, и xvalue — это expiring value. (Этот анализ я видел на clc++.moderated, но сейчас его найти мне не удалось.)

Для lvalue здесь все просто:
    string lv{"abc"};
    foo1(lv);  // copy conductor for foo1 par
    foo2(lv);  // copy conductor for cpy inside foo2


prvalue:
    foo1(string{"abc"});  // nothing (copy/move constructor is elided)
    foo2(string{"abc"});  // nothing (foo2(&&))


xvalue:
    string xv1{"abc"};
    foo1(std::move(xv1));  // move constructor for foo1 par

    string xv2{"abc"};
    foo2(std::move(xv2));  // nothing (foo2(&&))


Таким образом видно, что для функции с параметром по значению происходит дополнительный вызов move конструктора, когда мы явно передаем && ссылку. В остальных случаях foo1 и foo2 дают один и тот-же эффект. Если для нашего типа операция move constructor достаточно дешевая, то разницей в эффективности функций foo1 и foo2 можно пренебречь.

Когда это может быть полезно? Если подумать перегрузки функции для const & и && случаются не так уж и редко. Например, вот эта всем знакомая функция:
    Obj & operator=(Obj const & other) { … }
    Obj & operator=(Obj && other) { … }


Два оператора присваивания можно заменить на один:
    Obj & operator=(Obj other)
    {
        // with typical implementation:
        other.swap(*this);
        return *this;
    }


Или вот пример (немного модифицированный), который часто встречается в презентациях Страуструпа, когда он рассказывает о rvalue ссылках:
    Matrix operator+(Matrix const & m1, Matrix const & m2) { … }
    Matrix operator+(Matrix && m1, Matrix const & m2) { … }

    // single overload, practically equivalent in performance:
    Matrix operator+(Matrix m1, Matrix const & m2) { … }


Я даже рискну сделать следующий неожиданный вывод: всегда, когда есть необходимость иметь две перегрузки функции для const & и && параметров, их можно заменить на одну с передачей параметров по значению. Т.е. с практической точки зрения в большинстве случаев передавать параметры по rvalue ссылке вообще не нужно.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.