STL своп по возвращении? - PullRequest
       26

STL своп по возвращении?

8 голосов
/ 19 января 2011

извините за такой длинный вопрос, но я стараюсь быть максимально ясным.Это как-то следует моему предыдущему вопросу о строках в C ++ .Я пытаюсь выяснить, как я могу вернуть std :: string из функции без избыточного выделения памяти, , не полагаясь на NRVO .Причины, по которым я не хочу полагаться на NRVO:

  • , он не поддерживается компилятором, который мы сейчас используем
  • , даже если он поддерживается, он не всегда может быть включенв режиме отладки
  • в некоторых случаях может произойти сбой ( пример )

Обратите внимание, что мне нужно решение, совместимое с C ++ 03 (без C ++0x rvalue ссылок, таким образом, к сожалению ...)

Простейший способ сделать это - передача по ссылке и выполнить std :: swap, например

void test(std::string& res)
{
    std::string s;
    //...
    res.swap(s);
}

Но это более естественнои часто более удобно возвращать по значению, чем передавать по ссылке, поэтому я хочу добиться следующего:

std::string test()
{
    std::string s;
    //...
    return SOMETHING(s);
}

В идеале это просто сделало бы swap с "возвращаемым значением", но я нене вижу, как это сделать в C ++.Уже есть auto_ptr, который перемещается вместо копирования, и я мог бы на самом деле использовать auto_ptr<string>, но я бы хотел избежать динамического выделения самого строкового объекта.

Моя идея состоит в том, чтобы как-то «пометить» строкуобъект, который возвращается из функции, чтобы разрешить перемещение своих данных при вызове конструктора копирования по возвращении.Итак, я получил этот код, который делает именно то, что я хочу:

struct Str
{
    struct Moveable
    {
        Str & ref;
        explicit Moveable(Str & other): ref(other) {}
    };

    Str() {}
    Str(const std::string& other) : data(other) {} // copy
    Str(Moveable& other) { data.swap(other.ref.data); } // move

    Moveable Move()
    {
        return Moveable(*this);
    }

    std::string data;
};

Str test()
{
    Str s;
    //...
    return s.Move(); // no allocation, even without NRVO
}

Итак ... Имеет ли все это смысл, или есть некоторые серьезные проблемы, которые я упускаю?(Я не уверен, если нет проблемы жизни, например).Может быть, вы уже видели такую ​​идею в библиотеке (книга, статья ...) и могли бы дать мне ссылку на нее?

РЕДАКТИРОВАТЬ: Как заметил @rstevens, этот код специфичен для MSVC и выиграл 't компилируется под g ++, который не любит временную неконстантность.Это является проблемой, но давайте просто предположим, что эта реализация зависит от MSVC.

Ответы [ 3 ]

4 голосов
/ 19 января 2011

Реализация boost использует внутреннюю эмуляцию семантики перемещения для таких библиотек, как Boost.Thread . Вы можете посмотреть на реализацию и сделать что-то похожее.

Редактировать: на самом деле идет активная разработка библиотеки Boost.Move , так что вы уже можете начать ее использовать.

1 голос
/ 19 января 2011

Вы проверяли этот код на g ++?

Поскольку вы вызываете Str (Movable &) с временным объектом (возвращаемым s.Move ())!

Это не соответствует стандарту и не поддерживается g ++. Это поддерживается MSVC! (MS называет это функцией ...).

0 голосов
/ 19 января 2011

Вы действительно определили, что возврат по значению является проблемой производительности в вашем приложении? Это кажется самым простым и легким способом, и когда вы переходите на более современный компилятор, вы можете использовать rvalue ссылки.

Я не могу ответить на вопрос относительно порядка уничтожения s против ссылки Movable. Для вашего компилятора вы можете поместить код в различные конструкторы и деструкторы, чтобы увидеть, каков порядок. Даже если это выглядит хорошо, я все равно рассмотрю использование одного из нормальных шаблонов, которые вы обрисовали, хотя бы для того, чтобы предотвратить путаницу читателя и возможную поломку альтернативного компилятора.

...