Можно ли иметь реализацию члена для обмена броском?

Общая рекомендация при написании классов (с использованием идиомы копирования и обмена) заключается в предоставлении функции-члена без броска свопинга. ( Действующий C++, 3-е издание, пункт 25 и другие ресурсы)

Однако что, если я не могу предоставить гарантию nothrow, потому что в моем классе используется сторонний член класса, который не предоставляет операции подкачки?

// Warning: Toy code !!!

class NumberBuffer {
public:
    ...
    void swap(NumberBuffer& rhs);

public:
    float* m_data;
    size_t m_n;
    CString m_desc;
};

void swap(NumberBuffer& lhs, NumberBuffer& rhs) {
    lhs.swap(rhs);
}

void NumberBuffer::swap(NumberBuffer& rhs) {
    using std::swap;
    swap(m_data, rhs.m_data);
    swap(m_n, rhs.m_n);
    swap(m_desc, rhs.m_desc); // could throw if CString IsLocked and out-of-mem
}

Своп CString не может быть выполнен без броска, так что есть вероятность, что своп может потерпеть неудачу.

Примечание: для редких сторонних классов использование смарт-ptr (pimpl) было бы возможным, но -

Примечание: CString - хороший пример, поскольку никто в здравом уме (?) Не начал бы удерживать всех членов концептуально простого и вездесущего класса, такого как CString, через pimpl (smart ptr), потому что это действительно выглядело бы ужасно - и с другой стороны, нет (в краткосрочном и среднесрочном плане) шансов изменить CString, чтобы полностью заменить своп без бросков.

Итак, нормально ли иметь потенциально бросающуюся функцию-член подкачки, если вы не можете с этим поделать? (Или вы знаете пути решения этой головоломки?)

Редактировать: И: Можно ли использовать бросающий элемент обмена с идиомой копирования и обмена, чтобы обеспечить основную гарантию, если не сильную гарантию?

3 ответа

Решение

Итак, нормально ли иметь потенциально бросающуюся функцию-член подкачки, если вы не можете с этим поделать? (Или вы знаете пути решения этой головоломки?)

Нет ничего плохого в том, чтобы иметь swap функция, которая может потенциально бросить, но будьте осторожны, что без сильного исключения гарантируют swapего нельзя использовать для обеспечения безопасности исключений, то есть его можно использовать только как swap (то есть, забудьте об идиоме копирования и замены для этого конкретного класса как о способе предоставления гарантии строгого исключения... но вы все равно можете использовать его, чтобы уменьшить объем кода - и документ, который не является исключением безопасный)

Кроме того, вы можете переместить CString в умный указатель, который предлагает не бросать swap (или, по крайней мере, гарантия сильного исключения), не очень хорошее решение, но оно, по крайней мере, будет безопасным для исключения. Наконец, вы можете отойти от CString в общем, с помощью любой другой строковой библиотеки, которая предоставляет все, что вам нужно, и предлагает операцию замены без бросков.

Там нет ничего по сути неправильно с броском swap, это просто менее полезно, чем безбросковая версия.

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

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

Вы можете легко сделать это.

void NumberBuffer::swap(NumberBuffer& rhs) throw()
{
    try
    {
        std::swap(m_desc, rhs.m_desc);   //could throw
        std::swap(m_data, rhs.m_data);
        std::swap(m_n, rhs.m_n);
    }
    catch(...)
    {
    }
}

Конечно, это не реальное решение проблемы, но теперь вы по крайней мере получили своп без броска;)

Другие вопросы по тегам