C++零大小动态数组与私有构造类的概念判定:编译器分歧谁正确?
C++零大小数组动态分配的标准符合性判定
我定义了一个C++概念heap_constructible,用于检查对象数组是否可动态分配:
template< class T, int N > concept heap_constructible = requires() { delete[] new T[N]; };
意外发现,当数组大小N=0且类的构造函数不可访问(例如private修饰)时,不同编译器的判定结果存在分歧:
class A { A(); }; static_assert( !heap_constructible<A, 5> ); // 所有编译器均通过 static_assert( heap_constructible<A, 0> ); // 仅GCC通过
各编译器表现:
- GCC允许对A类对象进行零大小分配,断言通过
- Clang报错:
calling a private constructor of class 'A',断言失败 - MSVC判定该概念不满足,断言失败
对概念稍作修改,新增空大括号列表初始化后:
template< class T, int N > concept heap_constructible1 = requires() { delete[] new T[N]{}; // 新增空大括号列表 };
此时的断言结果:
static_assert( heap_constructible1<A, 0> ); // GCC和Clang均通过,MSVC仍判定为false
标准符合性分析
根据C++标准的相关规则:
new T[0]的合法性:当分配零大小数组时,不会创建任何对象,因此也不会调用T的构造函数。这意味着该操作的合法性仅要求T是完整类型,与T的构造函数可访问性无关。- GCC的行为符合这一规则:它正确判断零大小分配不需要访问构造函数,因此
heap_constructible<A,0>的断言通过。 - Clang最初的错误在于错误地检查了构造函数的可访问性,不符合标准要求。
- GCC的行为符合这一规则:它正确判断零大小分配不需要访问构造函数,因此
new T[0]{}的合法性:带空大括号的值初始化同样不会创建任何对象,因此也不需要访问T的构造函数。Clang在这里修正了判断,使得heap_constructible1<A,0>的断言通过,这一行为符合标准。MSVC的行为:无论是否使用大括号初始化,MSVC都判定概念不满足,这不符合标准——零大小数组分配不需要依赖构造函数的可访问性。
总结:
- GCC的两种判定均符合C++标准
- Clang仅对修改后的
heap_constructible1<A,0>判定符合标准,原始概念的判定不符合 - MSVC的两种判定均不符合标准
内容的提问来源于stack exchange,提问作者Fedor
相关产品推荐
相关产品推荐

