在std::vector中使用C风格数组——这是MSVC的Bug吗?
C++20下MSVC编译
std::vector<const char*[2]>失败是否为编译器Bug? 问题背景
我正在将遗留代码库迁移至C++20,遇到如下代码:
size_t someCount; // 值来自其他地方 … std::vector<const char *[2]> keyValues(someCount);
由于后续需传递给不受控制的外部API,无法改为std::vector<std::array<const char *, 2>>。该代码在Clang、GCC及未启用C20的MSVC中可编译,但在C20的MSVC中失败。
我推测这与T的DefaultInsertable要求有关:C++20开始STL对该类型使用std::construct_at,可能导致MSVC出现回归。标准规定若allocator_traits<A>::construct(m, p)合法则类型为DefaultInsertable,我测试对应代码在各编译器均可编译,故认为原构造函数应合法。
编译器错误信息
C:/data/msvc/14.34.31931-Pre/include\xutility(218): error C2440: 'return': cannot convert from 'const char **' to '_Ty (*)' with [ _Ty=const char *[2] ] C:/data/msvc/14.34.31931-Pre/include\xutility(218): note: Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or parenthesized function-style cast C:/data/msvc/14.34.31931-Pre/include\xmemory(673): note: see reference to function template instantiation '_Ty (*std::construct_at<_Objty,,0x0>(_Ty (*const )) noexcept)' being compiled with [ _Ty=const char *[2], _Objty=const char *[2] ]
回答
是的,这是MSVC的编译器Bug。
原因解析
- C++20构造逻辑的变化:C++20标准要求容器默认构造元素时使用
std::construct_at替代旧的分配器construct调用,但MSVC对数组类型的std::construct_at实现存在错误。 - 类型转换违规:错误信息显示,MSVC的
construct_at实现尝试将const char**(数组首元素的指针)转换为const char*(*)[2](数组指针),这属于非法的跨类型转换——标准只允许数组指针隐式转换为指向首元素的指针,反向转换必须显式使用reinterpret_cast,而其他编译器的construct_at实现正确处理了数组类型的构造逻辑。 - DefaultInsertable的合规性:你测试的
allocator_traits<A>::construct(m, p)合法是符合标准的——const char*[2]是平凡类型,默认构造不需要任何操作,因此该类型满足DefaultInsertable要求,MSVC的行为违背了标准规定。
临时解决方案
如果暂时无法等待MSVC修复,可尝试以下两种方法:
- 自定义分配器:实现一个自定义分配器,手动处理数组类型的构造逻辑,绕过MSVC的
std::construct_at错误实现; - 安全转换替代:先用
std::vector<std::array<const char*, 2>>存储数据,在传给外部API时通过reinterpret_cast<const char*(*)[2]>(vec.data())转换指针——标准保证std::array与原生数组内存布局完全一致,因此该转换是安全的。
内容的提问来源于stack exchange,提问作者Lukas Barth
相关产品推荐
相关产品推荐

