9
template <class T>
struct Obj {
  // Plain Old Data for T
  using InternalPod = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;

  InternalPod value_pod_;

  template<class... Args>
  Obj(Args&&... args) { // my constructor
    // placement new: construct the value in the statically allocated space
    new (&value_pod_) T(std::forward<Args>(args)...); // <- can this whole expression throw if the constructor of T doesn’t throw?
  }
}

Normal new can throw if the allocation fails or if the construction fails (correct me if there are other cases), but since placement new doesn’t allocate any space, can the new expression throw if the constructor of T doesn’t throw?

I.e. is the following noexcept specification correct and safe?

Obj(Args&&... args) noexcept(noexcept(T(std::forward<Args>(args)...))) {
  new (&value_pod_) T(std::forward<Args>(args)...);
}
bolov
  • 72,283
  • 15
  • 145
  • 224

1 Answers1

16

The placement new from <new> is declared to be noexcept according to 18.6 [support.dynamic] paragraph 1:

... void* operator new (std::size_t size, void* ptr) noexcept; ...

When using a new expression the system does exactly two things:

  1. It calls the appropriate version of the operator new() to obtain memory. If memory allocation fails it should throw std::bad_alloc for an operator new() without a noexcept qualification and return nullptr otherwise.
  2. If a non-nullptr is returned, the expression then calls the constructor of the type in the new expression. If this construction fails with an exception, the operator delete() matching the called operator new() is called with the result from that operator new().

Since memory allocation can't fail, the only option to get an exception is from the constructor of the type.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I guess the question is if the new expression does anything else that can throw beyond calling the constructor – bolov Jun 17 '14 at 14:33
  • The only things calling placement new does is call the above `operator new()` followed by calling the constructor of the object in the `new` expression (and it would call a matching `operator delete()` if the construction throws an exception). – Dietmar Kühl Jun 17 '14 at 14:37
  • The placement new expression also checks the address returned by `operator new` against `nullptr` before calling the constructor. It won't make a difference in this case, of course, but it's good to remember. – Potatoswatter Jun 17 '14 at 14:45
  • 1
    What if the actual arguments passed to `Obj::Obj` require a user defined conversion, which might throw? – James Kanze Jun 17 '14 at 15:27
  • @JamesKanze then the constructor throws… I think – bolov Jun 17 '14 at 15:35
  • @bolov Then the conversion operator throws. – James Kanze Jun 17 '14 at 17:26