I added FooOld, which corresponds to the unsafe design I want to replace. https://godbolt.org/z/15q9EqnfP
Thanks. I note that as written, FooOld actually has the same problem even if the argument is not taken by value.
template <class T> struct FooOld2 { FooOld2(bool b, T const& targ) : t_(targ) { if (b) throw 1; } FooOld2(bool b, T&& targ = {}) : t_(std::move(targ)) { if (b) throw 1; } private: T t_; };
There might be a difference between passing by value or not, but for that, the exception would have to be thrown from a base or previous member initialization, and not the constructor body.
Yes, FooOld2 is also unsafe, but you can use a rollback guard to undo the move. FooOld3(bool b, T&& targ = {}) : t_(std::move(targ)) { rollback r(targ, t_); // moves t_ back to targ if not deactivated if (b) throw 1; r.deactivate(); } Or you can put all the throwing code into a lambda FooOld4(bool b, T&& targ = {}) : t_( [&](auto&& targ) { // to all the potentially throwing stuff here return targ; }(std::forward(targ)) ) { } The rollback version is also ok, but cannot be used when you pass by value. I don’t like the lambda version, it makes the code unreadable.