相关疑难解决方法(0)

什么是复制和交换习语?

这个成语是什么,什么时候应该使用?它解决了哪些问题?当使用C++ 11时,成语是否会改变?

虽然在许多地方已经提到过,但我们没有任何单一的"它是什么"问题和答案,所以在这里.以下是前面提到的地方的部分列表:

c++ c++-faq copy-constructor assignment-operator copy-and-swap

1907
推荐指数
5
解决办法
34万
查看次数

Copy-and-Swap Idiom应该成为C++ 11中的复制和移动习惯吗?

本回答所述,复制和交换习惯用法如下实现:

class MyClass
{
private:
    BigClass data;
    UnmovableClass *dataPtr;

public:
    MyClass()
      : data(), dataPtr(new UnmovableClass) { }
    MyClass(const MyClass& other)
      : data(other.data), dataPtr(new UnmovableClass(*other.dataPtr)) { }
    MyClass(MyClass&& other)
      : data(std::move(other.data)), dataPtr(other.dataPtr)
    { other.dataPtr= nullptr; }

    ~MyClass() { delete dataPtr; }

    friend void swap(MyClass& first, MyClass& second)
    {
        using std::swap;
        swap(first.data, other.data);
        swap(first.dataPtr, other.dataPtr);
    }

    MyClass& operator=(MyClass other)
    {
        swap(*this, other);
        return *this;
    }
};
Run Code Online (Sandbox Code Playgroud)

通过将MyClass的值作为operator =的参数,可以通过复制构造函数或移动构造函数构造参数.然后,您可以安全地从参数中提取数据.这可以防止代码重复并有助于异常安全.

答案提到您可以在临时中交换或移动变量.它主要讨论交换.但是,交换(如果未由编译器优化)涉及三个移动操作,而在更复杂的情况下,还需要额外的额外工作.当你想要的时候,就是临时文件移动到assign-to对象中.

考虑这个更复杂的例子,涉及观察者模式.在这个例子中,我手动编写了赋值运算符代码.重点是移动构造函数,赋值运算符和交换方法:

class MyClass : Observable::IObserver …
Run Code Online (Sandbox Code Playgroud)

c++ assignment-operator move-semantics copy-and-swap c++11

23
推荐指数
3
解决办法
4942
查看次数

异常保证和按值传递

我最近在多种情况下遇到过这个问题,其中表达的一些观点让我感到惊讶。这是第一个简单的例子:

void f(std::vector<double> x) {};
Run Code Online (Sandbox Code Playgroud)

问题是:记录或描述f提供不抛出保证是否可以接受?同样,我怀疑由于异常不是从 f 的主体生成的,因此使用noexcept在技术上是合理的。但应该标记它吗noexcept?例如,一个优化版本以set某种方式发现添加模板化比较器不得抛出的要求很有用。它在编译时使用静态断言检测到这一点并导致错误。然而,有人可以为按值获取的向量编写一个比较器,并将其标记为 noexcept,并将其与此版本的set. 如果这导致了不良行为,那么这是容器作者的错吗?或者将比较器标记为 no except 的人?

再举一个涉及另一种异常保证类型的示例,请考虑:

void g(std::vector<double> x, std::unique_ptr<int> y);
Run Code Online (Sandbox Code Playgroud)

该功能能否提供强有力的保证?

std::vector<double> p{1.0, 2.0};
auto q = std::make_unique<int>(0);
bool func_successful = true;

try {
  g(p, std::move(q));
}
catch (...) {
  func_successful = false;
}

if (!func_successful)
  assert(q);
Run Code Online (Sandbox Code Playgroud)

我认为,如果断言可能失败,那么 g 不会提供强有力的保证,因为q在调用 g 之前立即不为空(记住std::move实际上并没有移动任何东西)。它可能会失败:参数评估的顺序未指定,因此可能首先构造 y 清空 q,然后构造 x 并抛出。即使从技术上讲它不是发生在函数体中,而是发生在调用函数的行为中,该函数仍然对此负责吗?

编辑:我要提到 Herb Sutter 在这里讨论了这个问题:https://youtu.be/xnqTKD8uD64 ?t=1h11m56s 。他说,将这样的函数标记为 noexcept 是“有问题的”;我希望得到更详细的答复。

c++

5
推荐指数
1
解决办法
386
查看次数