Вопрос

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

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

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

Самый простой способ сделать это — передать по ссылке и выполнить 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 и не будет компилироваться под g++, которому не нравятся неконстантные временные значения.Этот является проблема, но давайте просто предположим, что эта реализация специфична для MSVC.

Это было полезно?

Решение

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

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

Другие советы

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

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

Это не соответствует стандарту и не поддерживается g++.Поддерживается MSVC!(MS называет это особенностью...).

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top