Reputation: 16731
Is it allowed by the Standard to write decltype(::new (nullptr) T(std::declval< Args >()...))
or noexcept(::new (nullptr) T(std::declval< Args >()...))
? Particularly interested placement new
on nullptr
correctness. Considering following code:
#include <type_traits>
#include <utility>
struct S { S(int) { ; } ~S() = delete; };
struct A
{
template< typename T,
bool is_noexcept = noexcept(::new (nullptr) S(std::declval< T >())) >
A(T && x) noexcept(is_noexcept)
: s(new S(std::forward< T >(x)))
{ ; }
S * s;
};
static_assert(std::is_constructible< A, int >{});
static_assert(!std::is_constructible< A, void * >{});
Disabler typename = decltype(S(std::declval< T >()))
would need presence of destructor, but placement new
not.
Nevertheless unevaluated context of decltype
and operator noexcept
I wondering about conformance to the Standard. Because compiler may prove 100% incorectness of the tested expression.
Upvotes: 3
Views: 762
Reputation: 171403
In all published versions of the standard it is OK to pass a null pointer to placement new, and the compiler is required to handle that case, so the code is OK if it calls ::operator new(size_t, void*)
with a null pointer.
However, a class could have overloaded operator new(size_t, nullptr_t)
in which case your expression would use that, and so you can't know exactly what would happen (e.g. it might be deleted, or it might be noexcept(false)
).
In the working draft for C++17 it is undefined behaviour if a reserved placement allocation function returns null (this was changed following discussion arising from a question I asked here and on the committee lists). It's not clear whether that means that it is actually called and returns null, or whether even your usage would be undefined.
I would prefer not to risk it, and would use something that more clearly expresses the intention. Since what you are trying to do is test whether construction (but not destruction) can throw, by using a new
expression that won't throw, say exactly that, and use new(std::nothrow)
, or use an expression that uses a void*
that is not provably a null pointer: new (std::declval<void*>())
.
This avoids any confusion due to using nullptr
when the property you are testing is unrelated to nullptr
. Involving nullptr
just complicates things, by making the reader wonder if the use of nullptr
is significant, or if you're just being lazy and using that as a shorthand for "some void*
pointer value".
Upvotes: 5