在 decltype() 或 operator noexcept() 上下文中对 nullptr 使用 placement new

Using placement new on nullptr in decltype() or operator noexcept() context

标准允许写decltype(::new (nullptr) T(std::declval< Args >()...))noexcept(::new (nullptr) T(std::declval< Args >()...))吗?特别感兴趣的位置 new nullptr 正确性。考虑以下代码:

#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 >())) 需要存在析构函数,但放置 new 不需要。

然而,decltype 和运算符 noexcept 的上下文未评估,我想知道是否符合标准。因为编译器可能会证明测试表达式 100% 不正确。

在标准的所有已发布版本中,可以将空指针传递给 placement new,编译器需要处理这种情况,因此如果代码调用带有空指针的 ::operator new(size_t, void*) 是可以的指针。

但是,class 可能会重载 operator new(size_t, nullptr_t),在这种情况下,您的表达式将使用它,因此您无法确切知道会发生什么(例如,它可能被删除,或者它可能是 noexcept(false)).

在 C++17 的工作草案中,如果保留位置分配函数 returns 为 null(这在 a question I asked here and on the committee lists 引起的讨论后已更改),则为未定义行为。目前尚不清楚这是否意味着它实际上被调用并且 returns 为空,或者甚至您的用法是否未定义。

我宁愿不冒险,而是会使用更清楚地表达意图的东西。因为你要做的是测试构造(而不是破坏)是否可以抛出,通过使用不会抛出的 new 表达式,准确地说,并使用 new(std::nothrow),或使用表达式使用的 void* 不能证明是空指针:new (std::declval<void*>()).

当您正在测试的 属性 与 nullptr 无关时,这可以避免因使用 nullptr 而造成的任何混淆。涉及 nullptr 只会使事情复杂化,因为 reader 想知道 nullptr 的使用是否重要,或者您是否只是懒惰并将其用作 shorthand "some void* pointer value".