【问题标题】:would the pointer returned by new(size, value) Type[0] be legal and could it be used to build an array?new(size, value) Type[0] 返回的指针是否合法,是否可以用于构建数组?
【发布时间】:2026-02-01 08:25:01
【问题描述】:

标准说,5.3.4[expr.new]/7

当 direct-new-declarator 中表达式的值为零时,调用分配函数来分配一个没有元素的数组。

3.7.3.1[basic.stc.dynamic.allocation]/2

取消引用作为零大小请求返回的指针的效果是未定义的。

但是如果分配函数是用户定义的并且它知道它返回了一个有效的指针,那么取消引用它仍然是未定义的行为吗?标准可以强制用户代码的未定义行为吗?

我问的原因是另一个毫无意义的尝试初始化非默认构造类型的对象的动态数组。除了明显缺少delete[] 并且只能用[0] 调用之外,它还有什么问题?我是否正确使用了aligned_storage

#include <type_traits>
#include <stdexcept>
#include <memory>
#include <iostream>

struct T {
   int val;
   T() = delete;
   T(int i) : val(i) {}
   void* operator new[](std::size_t, std::size_t cnt, const T& t)
   {
       typedef std::aligned_storage<sizeof(t),
                    std::alignment_of<T>::value>::type buf;
       T* ptr = reinterpret_cast<T*>(new buf[cnt]);
       std::uninitialized_fill_n(ptr, cnt, t);
       return ptr;
    }
};

int main()
{
    T* a = new(100, T(7)) T[0]; // using zero is legal per 5.3.4/7

    std::cout << "a[0] = " << a[0].val << '\n' // but is this legal?
              << "a[1] = " << a[1].val << '\n'
              << "a[98] = " << a[98].val << '\n'
              << "a[99] = " << a[99].val << '\n';
    delete[] a; // free the 100 aligned_storages
}

试运行:http://ideone.com/iBW0z

使用 MSVC++ 2010 EE 也可以按预期编译和运行

【问题讨论】:

  • "我问的原因是又一次毫无意义的尝试初始化非默认构造类型的对象的动态数组。" 为什么std::vector 不够?它完全允许这样做。
  • @ildjarn:对于这个目的来说已经绰绰有余了,是的。我只是在探索语言的边界。
  • 我也不确定new 运算符返回的偏移量。我认为,对于new 所知道的一切,它可以使用等于100 * sizeof(T) 的未命名size_t 参数调用您的operator new,并将生成的缓冲区偏移100 * sizeof(T)。或者更现实地,用sizeof(size_t) 调用它并偏移sizeof(size_t) 来存储数组元素的计数(需要知道以后调用多少个dtor),而不是用0 的大小调用它。跨度>

标签: c++ c++11 standards-compliance array-initialization


【解决方案1】:

你的代码中有一个烦人的逻辑问题:

新的表达方式:

T* a = new(100, T(7)) T[0];

调用 T 的已删除默认构造函数 [expr.new]/17。 ;-(

std::vector&lt;T&gt; 现在肯定看起来不错... :-)

【讨论】:

  • 我明白了,它希望构造函数可以访问。每次我试图超越 C++ 时,它都会超越我。
  • 所以你可以直接调用 operator new,而不是使用 new 表达式。这基本上就是矢量的作用。但由于矢量已经做到了,我不确定激励用例是什么。除非您要求,否则矢量不需要默认可构造。
  • 动机是为了证明 new(...) T[0] 的合理性,动机是好奇心。
  • 好奇心是一种极好的动力! :-) 尝试将您的 operator new 移动到全局范围并直接调用它,而不是通过 new 表达式: T* a = static_cast(operator new[](sizeof(T)*0, 100, T(7 )));
【解决方案2】:

使用reinterpret_cast 的结果的唯一未定义行为是当强制转换恢复到其原始类型时,因此即使其他一切正常,您也已经拥有 UB。

如果你真的需要这个,为什么不创建一个函数来分配足够大的连续内存块,然后将news 一堆T 放入该内存中?

【讨论】:

  • 虽然我所做的只是增加指针并将其强制转换为 void,但我想最好将其定义为调用 get_temporary_buffer,它返回 T*,而不是新建一个aligned_storages?让它成为一个单独的函数意味着我只是在实现向量,这已经完成了。
最近更新 更多