STL 容器函数返回值

2022-01-24 00:00:00 return-value containers c++ stl

查看 STL 容器的成员函数时,我想到了一个奇怪的想法.为什么像 std::vector<T>::push_back(T) 这样的函数没有(可选的)返回值(迭代器甚至对附加对象的引用)?我知道像 inserterase 这样的 std::string 函数会返回迭代器,但这是出于显而易见的原因.我认为它通常会在这些函数调用之后保存第二行代码.

When looking over the member functions of the STL containers, an odd thought occurred to me. Why don't functions like std::vector<T>::push_back(T) not have an (optional) return value (iterator or even a reference to the appended object)? I know std::string functions like insert and erase return iterators, but that's for obvious reasons. I'd think it'd often save a second line of code that often follows these function calls.

我相信C++的设计者有一个很好的理由,请赐教:)

I'm sure the designers of C++ have a very good reason, please enlighten me :)

更新:我在这里包含一个真实世界的代码示例,它可以减少代码长度:

UPDATE: I'm including a real-world code example here where it could reduce code length:

if( m_token != "{" )
{
    m_targets.push_back( unique_ptr<Target>(new Dough(m_token)) );
    return new InnerState( *(m_targets.back()), this );
}

可以简化为

if( m_token != "{" )
    return new InnerState( *(m_targets.push_back( unique_ptr<Target>(new Dough(m_token)) )), this );

如果我假设 std::list::push_back 返回对添加元素的引用.代码有点重,但这主要是(两组括号)由于 unique_ptr 的构造函数和取消引用它.也许为了清楚起见,没有任何指针的版本:

If I assume std::list::push_back returns a reference to the added element. The code is a bit heavy, but that's mostly (two sets of parentheses) due to unique_ptr's constructor and dereferencing it. Perhaps for clarity a version without any pointers:

if( m_token != "{" )
{
    m_targets.push_back( Dough(m_token) );
    return new InnerState( m_targets.back(), this );
}

对比

if( m_token != "{" )
    return new InnerState( m_targets.push_back( Dough(m_token) ), this );

推荐答案

返回添加的元素,或者容器成员函数中的容器是不可能安全的.STL 容器大多提供"强力保证".退回被操纵的元素或容器将无法提供强有力的保证(它只会提供基本保证").这背后的原因是,返回某些内容可能会调用复制构造函数,这可能会引发异常.但是该函数已经退出,所以它成功完成了它的主要任务,但仍然抛出异常,这违反了强保证.你可能会想:那么让我们通过引用返回!"虽然这听起来是一个很好的解决方案,但它也不是完全安全的.考虑以下示例:

Returning the added element, or the container in container member functions is not possible in a safe way. STL containers mostly provide the "strong guarantee". Returning the manipulated element or the container would make it impossible to provide the strong guarantee (it would only provide the "basic guarantee"). The reason behind this is, that returning something could possibly invoke an copy-constructor, which may throw an exception. But the function already exited, so it fulfilled its main task successfully, but still threw an exception, which is a violation of the strong guarantee. You maybe think: "Well then lets return by reference!", while this sounds like a good solution, its not perfectly safe either. Consider following example:

MyClass bar = myvector.push_back(functionReturningMyClass()); // imagine push_back returns MyClass&

但是,如果复制赋值操作符抛出,我们不知道 push_back 是否成功,从而间接违反了强保证.尽管这不是直接违规.当然使用 MyClass&bar =//... 反而会解决这个问题,但是如果有人忘记了 &,容器可能会进入不确定状态,这会很不方便.

Still, if the copy-assignment operator throws, we dont know if push_back succeded or not, thus indirectly violating the strong-guarantee. Even though this is not a direct violation. Of course using MyClass& bar = //... instead would fix this issue, but it would be quite inconvenient, that a container might get into an indeterminate state, just because someone forgot a &.

后面有一个非常相似的推理std::stack::pop() 不返回弹出值的事实.取而代之的是 top() 以安全的方式返回最高值.调用 top 后,即使复制构造函数或复制赋值构造函数抛出,你仍然知道堆栈没有改变.

A quite similar reasoning is behind the fact that std::stack::pop() does not return the popped value. Instead top() returns the topmost value in a safe way. after calling top, even when a copy-constructor, or a copy-assignment constructor throws, you still know that the stack is unchanged.

我相信为新添加的元素返回一个迭代器应该是非常安全的,如果迭代器类型的复制构造函数提供不抛出保证(并且我所知道的每个人都这样做).

I believe returning an iterator for the newly added element should be perfectly safe, if the copy-constructor of the iterator-type provides the no-throw guarantee (and every i know of does).

相关文章