【问题标题】:Dynamic Zero-Length Arrays in C++C++ 中的动态零长度数组
【发布时间】:2018-10-10 20:51:32
【问题描述】:
#include <stdlib.h>

void *operator new[](size_t size, int n){
    if( size != 0 && n != 0 )
        return calloc(n, size);
    return calloc(1, 1);
}

int main(){

    int * p1;
    const int i = 0;

//  p1 = new (20)  int[i] ; // Case 1 (OK)
    p1 = new (20) (int[i]); // Case 2 (Warning)

    if( p1 == 0 )
        return 1;
    return 0;
}

此代码 (https://godbolt.org/g/hjo7Xn) 可以使用 Clang 6.0.0 成功编译,但是,GCC 7.3 发出警告说在 C++ 中禁止使用零长度数组。如果括号被删除(案例 1),警告就会消失。

与静态分配的零长度数组 (C++03:8.3.4/1) 不同,允许动态分配的零长度数组 (C++03:5.3.4/6)。然而,在 C++ 标准中,后者仅在遵循 new-expression 的两种可能语法路径之一时才被明确允许,即具有 new-type-id 的语法路径em> 且不带括号(案例 1)。

C++ 标准是否允许使用 new-expression 和第二个语法路径后的零长度数组,即 type-id em> 和括号(案例 2)?

唯一相关的引用是 C++03:5.3.4/5:

当分配的对象为数组时(即使用direct-new-declarator语法或new-type-idtype- id 表示一个数组类型),new-expression 产生一个指向数组初始元素(如果有的话)的指针。

措辞(if any) 将允许一个没有元素的数组,但是,似乎不清楚它是指这两种情况还是仅指具有new-type-id且没有括号的情况(案例一)。

提前致谢。

注意事项:

  1. ISO/IEC 14882:2003,第 8.3.4 节,第 1 段:

    如果 constant-expression (5.19) 存在,它应该是一个整数常量表达式,并且它的值应该大于零。

  2. ISO/IEC 14882:2003,第 5.3.4 节,第 6 段:

    direct-new-declarator 中的表达式 应具有非负值的整数或枚举类型 (3.9.1)。

  3. ISO/IEC 14882:2003,第 5.3.4 节,第 7 段:

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

  4. ISO/IEC 14882:2003,第 5.3.4 节,第 1 段:

    新表达式

    ::opt new new-placementopt new-type-id new-initializeropt

    ::opt new new-placementopt ( type-id ) new-initializeropt

  5. 虽然以上引用来自 C++03 标准,但据我所知,这个问题在新版本的 C++ 标准(C++11、C++14 和 C++17)中仍然不清楚.
  6. 有趣的 Herb Sutter 的 post 关于零长度数组。
  7. 示例中的代码是来自SolidSands' SuperTest 套件的稍微修改的测试。

【问题讨论】:

  • 只是好奇:如果您希望符合 C++11 标准,是否有理由引用 C++03 标准?因为网上有各种草稿,可能更容易引用/链接。
  • 自定义越野车operator new[] 是怎么回事?这跟什么有什么关系?
  • 没有来自 gcc 8 的警告。
  • @Rakete1111我对从 C++03 开始​​的合规性感兴趣。但你是对的,最好只链接到标准而不是直接引用。如果我发现每个部分(不是 PDF)C++03 标准的在线链接,我会更新帖子。
  • @Cheersandhth.-Alf 我认为问题是一样的,我离开它是因为我不想修改那么多原始测试(我已经在其他部分减少了它)。为什么它有问题?

标签: c++ arrays c++11 new-operator dynamic-memory-allocation


【解决方案1】:

使用括号,您有一个常规的 type-id,而不是称为支持动态数组大小的 new-type-id 的特殊语法。

从 C++17 开始,标准对 type-id 的使用没有特别规定,所以问题归结为您是否可以编写

auto main() -> int
{
    using Argh = int[0];
}

你不能,因为 type-id 的类型是根据虚构的“省略实体名称的那种类型的变量或函数的声明”(C ++17 §11.1/1),并且对于数组变量的声明,规则是“如果存在常量表达式 (8.20),则它应为 std::size_t 类型的转换常量表达式,其值应大于零”(C++17 §11.3.4/1)。


现在对此有相当多的解释。例如。如果没有这种合理的解释,最后的引用就不能合理地说数组大小必须是非负数并且可以表示为size_t。相反,在没有合理解释的情况下,它会字面上说像

这样的声明
int x[42];

无效(当然不是),必须表示为

int x[std::size_t(42)];

确定什么是合理的解释或过去并不容易。有人可能会问,这有意义吗?因此,对于上述情况,答案是否定的,人们可以放弃这种可能性。

但是,在某种程度上,C++14 和 C++17 越来越多,我发现早期可靠的技术失败了。但是,由于手头的问题是关于 C++03 功能的,我认为您可以相信这个答案。但如果是关于 C++14 或更高版本的问题,请记住,任何明显明确的答案都可能涉及一些主观解释,可能无法通过询问它是否有意义来解决。

【讨论】:

    【解决方案2】:

    不,零大小的情况不能使用带括号的type-id。大小为 0 的数组的行为(在当前草案中)仅针对 noptr-new-declarator ([expr.new]/7) 中的 表达式 给出。带有括号类型的new 尝试创建该类型的对象,并且没有大小为 0 ([dcl.array]/1) 的数组,甚至没有作为 type-id em> ([dcl.name]/1)。

    零大小的数组当然是一种常见的扩展,因此实际结果可能会有所不同。

    【讨论】:

    • 动态分配的数组也是一个对象。
    • @Cheersandhth.-Alf:是的,但我们在 /7 中有一个独特的想法,即可以“分配一个没有元素的数组”。
    • 我的意思是说,“带有括号类型的new 试图创建该类型的 object”这句话毫无意义,因为每个(成功) new 表达式求值创建一个对象。
    • @Cheersandhth.-Alf:new int[0] 创建了什么对象?无法命名它的唯一合理类型(即使通过decltype)。也许可以说它无论如何都存在,但它使“new 总是使一个对象”成为问题。
    • 这是一个int[],这是已知的最多的。 new int[argc] 创建的对象也是如此。
    猜你喜欢
    • 1970-01-01
    • 2012-09-06
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-22
    • 1970-01-01
    • 2010-10-15
    相关资源
    最近更新 更多