我目前正在使用constexpr数组,并且我注意到我无法使用/ std:c ++ 17或/ std:c ++在MSVC 19.15.26726下获得以下(有效)代码进行编译最新:
#include <array>
using array_type = std::array<unsigned int, 3>;
using iterator_type = array_type::const_iterator;
constexpr array_type arr{ { 1,2,3 } };
constexpr iterator_type getIteratorBefore(iterator_type it) {
return std::prev(it);
}
constexpr iterator_type test = getIteratorBefore(arr.end());
我从IntelliSense得到的所有忽略突出显示的错误和说std::array
的错误是模棱两可的(似乎与同一文件中的某些奇怪的array()
函数冲突),我得到以下信息最后一行中的编译器错误:
error C4146: unary minus operator applied to unsigned type, result still unsigned
error C4308: negative integral constant converted to unsigned type
warning C4307: '+': integral constant overflow
它可以在gcc(x86-64 gcc(trunk))和MSVC(x86-64 edit:MSVC Pre 2018 with / std:c ++ 17 works)下的编译器资源管理器中很好地编译(未测试其他版本)。
我很没主意。当我将其放入main方法中时,会编译相同的代码,因此constexpr范围似乎有问题。
答案 0 :(得分:3)
我可以使用VS 2017 15.8.1和带有/ std:c ++ 17或/ std:c ++ latest的最新15.9.0 Preview 1.0进行复制。
仅在调试配置中,_ITERATOR_DEBUG_LEVEL
不等于0
时才会出现问题。
通过MSVC附带的STL代码,我们可以看到_Array_const_iterator
有两种不同的实现,具体取决于_ITERATOR_DEBUG_LEVEL
。如果_ITERATOR_DEBUG_LEVEL
不等于0
,则迭代器存储指向数组的基本指针和类型为_Idx
的索引变量std::size_t
。否则,它仅存储一个指针。
部分问题是由_Array_const_iterator::operator+=()
引起的,它由std::prev()
间接调用,其参数值为-1
:
_CONSTEXPR17 _Array_const_iterator& operator+=(const ptrdiff_t _Off)
{ // increment by integer
_Verify_offset(_Off);
_Idx += _Off; //<-- error C4308
return (*this);
}
引起错误C4308的原因是_Idx
是无符号的,而_Off
是有符号的,而_Off
的实际(文字)值为负。
更简单的测试用例:
constexpr unsigned Test(unsigned x, int d) { x += d; return x; }
constexpr auto test1 = Test( 5, -1 ); //<-- error C4308
constexpr auto test2 = Test( 5, 1 ); //<-- OK
对test1
的赋值也会产生错误C4308和警告C4307。我不确定C4146,可能只是跟进错误。
在constexpr
上下文中混合使用有符号和无符号类型时,MSVC似乎比GCC更严格。
解决方案可能是MSFT将成员变量_Idx
的类型更改为ptrdiff_t
。随时提交错误:-)。
定义_ITERATOR_DEBUG_LEVEL = 0
或替换std::prev()
调用:
constexpr iterator_type getIteratorBefore(iterator_type it) {
return --it;
}