是否允许标准编写decltype(:: new(nullptr)T(std :: declval< Args>()…))或noexcept(:: new(nullptr)T(std :: declval< Args) >()...))?特别感兴趣的是在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>()))需要存在析构函数,但不需要放置新的.
然而,无法评估decltype和运算符的背景,我不知道是否符合标准.因为编译器可能证明测试表达式的100%incorectness.
解决方法:
在标准的所有已发布版本中,可以将空指针传递给placement new,并且编译器需要处理该情况,因此如果它调用:: operator new(size_t,void *)为null,则代码是正常的指针.
但是,一个类可能重载了operator new(size_t,nullptr_t),在这种情况下你的表达式会使用它,因此你无法准确知道会发生什么(例如它可能被删除,或者它可能是noexcept(false)) .
在C17的工作草案中,如果保留的放置分配函数返回null,则它是未定义的行为(这在a question I asked here and on the committee lists引起的讨论之后发生了变化).目前尚不清楚这是否意味着它实际上被调用并返回null,或者甚至您的使用是否未定义.
我宁愿不冒险,也会使用更明确表达意图的东西.因为你要做的是测试构造(但不是破坏)是否可以抛出,通过使用一个不会抛出的新表达式,准确地说,并使用new(std :: nothrow),或者使用一个使用的表达式void *,不可证明是空指针:new(std :: declval< void *>()).
当您测试的属性与nullptr无关时,这可以避免因使用nullptr而导致的混淆.涉及nullptr只会使事情复杂化,让读者想知道nullptr的使用是否重要,或者你是否只是懒惰并将其用作“某些void *指针值”的简写.