std :: vector是否违反严格的异常安全要求?

时间:2018-12-20 06:06:08

标签: c++ c++-standard-library exception-safety

我特别是在谈论函数std::vector::emplace(),据我所知,该函数必须提供强大的异常保证。也就是说,此函数在内部抛出异常的情况下一定不能有任何副作用。 我一直在这里寻找有关标准容器中异常安全性的信息,并且我可以看到一些对以下C ++标准摘录的引用。

23.2.1 / 10:

  

除非另有规定(请参见23.2.4.1、23.2.5.1、23.3.3.4和23.3.6.5),否则本条款中定义的所有容器类型均满足以下附加要求:

     

-如果在插入单个元素时insert()或emplace()函数引发异常,则该函数无效。

因此,是的,无论发生什么情况,标准容器以及其中的std::vector都不得因insert() / emplace()操作而改变。现在,观察以下课程。

int ObjectID;

class Foo
{
public:
    Foo()
        : ID(++ObjectID) // just some ID to distinguish different objects
    {
        std::cout << "default ctor" << std::endl;
    }

    Foo(const Foo& other)
        : ID(other.ID)
    {
        std::cout << "copy ctor" << std::endl;
    }

    Foo& operator=(const Foo& other)
    {
        ID = other.ID;
        std::cout << "copy assignment operator" << std::endl;
        return *this;
    }

    Foo(Foo&& other)
        : ID(other.ID)
    {
        std::cout << "move ctor" << std::endl;
    }

    Foo& operator=(Foo&& other)
    {
        ID = other.ID;
        std::cout << "move assignment operator" << std::endl;
        throw std::exception(); // yes, move assignment operator does throw!
        return *this;
    }

    int GetNumber() const { return ID; }

private:
    int ID;
};

现在让我们尝试使std::vector违反标准。

void PrintV(const std::vector<Foo>& v)
{
    std::cout << "v.size() = " << v.size() << std::endl;
    for (size_t i = 0; i < v.size(); ++i)
        std::cout << "v[" << i << "] = " << v[i].GetNumber() << std::endl;
}

int main()
{
    std::vector<Foo> v;
    v.reserve(4);

    v.emplace_back();
    v.emplace_back();
    v.emplace_back();

    // let's have a look at the vector, it must be fine now    
    PrintV(v);

    try
    {
        v.emplace(v.begin());
    }
    catch (const std::exception& ex)
    {
        std::cout << "an exception has been caught!" << std::endl;
    }

    // look! the vector has changed after an exception
    // was thrown though it shouldn't have
    PrintV(v);
}
简而言之,这段代码首先为一个向量中的4个元素分配了足够的容量,然后在其中添加了3个元素(通过将它们换回来),然后在开始处插入了一个新元素,这导致将所有其他元素转移到对。这就是我在输出中得到的。

default ctor
default ctor
default ctor
v.size() = 3
v[0] = 1
v[1] = 2
v[2] = 3
move ctor
move assignment operato                                                                                                                                                           
an exception has been caught!
v.size() = 4
v[0] = 1
v[1] = 2
v[2] = 2
v[3] = 3

一切都很好,直到最后一个emplace()发生为止。调用之后,我们可以看到std::vector的实现是如何首先从第三个元素移动构造第四个元素,然后在引发异常时开始将第二个元素移动分配给第三个元素。如您所见,向量结果发生了变化,因此功能emplace()具有副作用,尽管不需要这样做。如果我使用insert()而不是emplace(),也会发生同样的情况。

这种违反标准的原因是什么?在这种情况下实现强大的异常安全性是否太昂贵?还是我在这里理解错了?

0 个答案:

没有答案