【问题标题】:Is it OK to have a throwing swap member-implementation?有一个抛出交换成员实现可以吗?
【发布时间】:2011-12-01 11:05:07
【问题描述】:

编写类时的一般准则(使用复制和交换习惯用法)是提供非抛出交换成员函数。 (Effective C++, 3rd edition, Item 25 和其他资源)

但是,如果因为我的班级使用不提供交换操作的第 3 方班级成员而无法提供 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 交换不能不抛出,因此交换失败的可能性很小。

注意:对于罕见的第 3 方课程,可以选择使用智能 ptr (pimpl),但是 --

注意:CString 是一个很好的例子,因为他头脑正常的人 (?) 不会开始通过 pimpl (smart ptr) 来持有概念上简单且无处不在的类的所有成员,例如 CString,因为这看起来真的很可怕——而且在另一方面,没有(短期到中期)机会修改 CString 以允许完全无抛出交换。

那么,如果你帮不上忙,是否可以有一个潜在的抛出交换成员函数? (或者你知道解决这个难题的方法吗?)

编辑:并且:如果不是强保证,是否可以将 throwing swap 成员与 copy-and-swap 习语一起使用以提供基本保证?

【问题讨论】:

  • 不是一个答案,但如果你能离开CString,那么问题就会消失......
  • 请注意,在类似的主题上,当您尝试为您的班级实施 C++11 迁移时,您将在相当短的几年后遇到同样的问题。如果成员不能被 nothrow-swapped,那么它很可能也不会是 nothrow-movable,因为两者非常相似。

标签: c++ swap exception-safe copy-and-swap exception-safety


【解决方案1】:

你可以很容易地让它没有:

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(...)
    {
    }
}

当然,这不是问题的真正解决方案,但现在你至少得到了你的非抛出交换;)

【讨论】:

  • 除了那个swap没有履行它的合同并且默默地失败了!发现这些错误的快乐时光!我建议您将您的示例限定为如何这样做或删除答案。谢谢!
  • @Martin 这并不是太严肃,我意识到对此发表评论会更好,但请随意投反对票。
  • 鉴于throw() 异常规范,如果没有try/catch 会更好。这样,编译器将安排std::terminate 在抛出时被调用,至少在您没有处于某些不符合MSVC 模式的情况下。仍然可能不是调用者想要的,但至少很明显有问题。
  • "这不是很严重" 这很明显是 IMO。有“有趣”的按钮吗? (如 /.)
【解决方案2】:

那么,如果你不能帮助它,是否可以有一个潜在的抛出交换成员函数? (或者你知道解决这个难题的方法吗?)

拥有swap 函数可能引发潜在的抛出本质上错误,但请注意,如果没有swap 中的强大异常保证,它就不可能用于提供异常安全性,也就是说,它只能用作swap(也就是说,忘记该特定类的复制和交换习语作为提供强异常保证的一种方式......但您仍然可以使用它来减少代码量——并证明它不是异常安全的)

或者,您可以将CString 移动到提供不抛出swap 的智能指针(或至少是强异常保证),这不是一个好的解决方案,但它至少是异常安全的.最后,您可以完全摆脱 CString,而使用任何其他字符串库来提供您需要的任何内容并提供无抛出交换操作。

【讨论】:

  • +1 你能画出从异常安全到线程安全的一点联系吗?或者这是一个错字?
  • @ChristianRau:考虑到上下文,可能是错字......事实上,我下意识地阅读了 exception 安全性:)
  • @ChristianRau:这是一个错字,抱歉。我有点累了:)
  • David - 是的,没有强有力的保证,据我所知,没有基本保证 (?),除非您非常小心。你怎么看?这种交换可以用在复制和交换成语中吗?还是必须添加一些 try-catch-revert 东西来提供基本保证? (也许您可以编辑您的答案以包含此内容。)
  • @Martin:你说得对,这个swap 不能用于提供any异常保证。基本保证可以通过包装在 try/catch 中来实现,但这与任何随机代码段基本相同......
【解决方案3】:

投掷swap 本身并没有什么问题,它只是不如无投掷版本有用。

为了提供强大的异常保证,复制和交换习语不需要swap 是不抛出的。 swap 只需提供强异常保证即可。

难点在于,如果不能提供不抛保证,也很可能不能提供强异常保证。使用临时和三个副本的朴素交换只提供基本保证,除非复制操作提供不抛出保证,在这种情况下交换也是不抛出的。

【讨论】:

  • 优秀的分数wrt。无投与强。如果只有一个成员不提供不投掷保证,那么只需先交换(复制)该成员即可获得强保证。如果有不止一个成员,我认为一个人不走运。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-10
  • 1970-01-01
  • 2019-03-14
  • 1970-01-01
  • 2011-12-10
  • 1970-01-01
  • 2011-05-09
相关资源
最近更新 更多