C++中的动态零长度数组

11
#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而不使用括号的路径(情况1),后者才被明确允许。
C++标准是否允许使用带有括号的type-id(情况2)来跟随零长度数组的new-expression第二个语法路径?
唯一相关的引用是C++03:5.3.4/5:
当分配的对象是数组(即使用direct-new-declarator语法或new-type-idtype-id表示数组类型时),new-expression将产生指向数组的初始元素(如果有的话)的指针。

(如果有)这个措辞允许一个没有元素的数组,但是它似乎不清楚它是指两种情况还是只指没有括号的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中的expression应具有整数或枚举类型(3.9.1)并具有非负值。
  3. ISO/IEC 14882:2003第5.3.4节第7段:
    direct-new-declarator中的expression的值为零时,调用分配函数以分配一个没有元素的数组。
  4. ISO/IEC 14882:2003第5.3.4节第1段: new-expression: ::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标准?因为有许多草案可以在线引用或链接,这可能更容易。 - Rakete1111
自定义buggy的operator new[]是什么意思?这和任何事情有关系吗? - Cheers and hth. - Alf
1
没有来自gcc 8的警告。 - Sam Varshavchik
@Rakete1111,我对C++03及以后的合规性很感兴趣。但你说得对,最好只是链接到标准而不是直接引用。如果我找到一个可以按部分链接的在线C++03标准(不是PDF),我会更新帖子。 - José Luis
@Cheersandhth.-Alf 我认为问题是一样的,我没有修改原始测试太多(我已经在其他部分减少了它)。为什么有bug呢? - José Luis
@JoséLuis:如果返回,它不能保证至少返回请求的字节数。 - Cheers and hth. - Alf
2个回答

2

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

截至C++17,标准没有为此使用类型标识符做出特殊规定,因此问题归结为是否可以编写

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或更高版本的内容,则请记住,任何明显清晰的答案可能涉及一些主观解释,这些解释可能无法通过询问是否有意义来解决。

0

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

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


动态分配的数组也是一个对象。 - Cheers and hth. - Alf
@Cheersandhth.-Alf:是的,但我们在/7中有一个独特的想法,即可以“分配一个没有元素的数组”。 - Davis Herring
我的意思是,语句“A new with a parenthesized type attempts to create an object of that type”是没有意义的,因为每个(成功的)new表达式求值都会创建一个对象。 - Cheers and hth. - Alf
1
@Cheersandhth.-Alf:new int[0] 创建了什么对象?它唯一合理的类型无法命名(即使通过 decltype 也不行)。或许可以说它仍然存在,但这使得“new 总是创建一个对象”的说法值得怀疑。 - Davis Herring
它是一个 int[],这就是已知的最多信息。这也适用于由 new int[argc] 创建的对象。 - Cheers and hth. - Alf
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接