On 24/09/2014 21:52, Aaron Levy wrote:
Yes I saw that the documentation says these are exception safe and understandably the arguments are forwarded to the constructors. I was just trying to understand the scenarios under which code like the example below cause exception-safety issues.
boost::ptr_vector<Foo> vec; push_back(vec)(new Foo)(new Foo);
My reasoning was that the nth "new Foo" gets evaluated after the n-1 "new Foo" expressions which by then are already part of the container.
That is not correct. The compiler is free to evaluate the independent subexpressions in any order; it is only constrained by dependent expressions. For example, the below code is a valid rewrite of the above by the compiler internals: boost::ptr_vector<Foo> vec; Foo *tmp2 = new Foo; auto tmp3 = push_back(vec); Foo *tmp1 = new Foo; auto tmp4 = tmp3(tmp1); tmp4(tmp2); In particular note that if the second "new Foo" (to tmp1) throws -- which is possible due to out-of-memory even if for no other reason -- then the pointer in tmp2 will be leaked, as it hasn't yet been assigned to the container. The same can occur if push_back or if tmp3() throw. The compiler is free to allocate tmp1 and tmp2 in any order; the only constraints are: 1. It must call their constructors after the constructor of vec is called. 2. It must create tmp1 before it can call tmp3 to create tmp4. 3. It must create tmp2 before it can call tmp4. Similarly if there were any parameters being passed to the Foo constructor (whether using this method or using constructor forwarding), temporary values for these could be created in any order -- not just out of parameter order but also not in the order of constructing the objects they're being passed to. It's the same principle as this: f(a(), b(), c(), d()); In the above, the compiler can call a,b,c,d in any order. The only constraint is that it must call all four of them before it can call f.