我目前正在使用constexpr数组,并且我注意到我无法使用/std:c++ 17或/std:c++ latest在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范围似乎是一个问题。
最佳答案
复制
我可以使用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);
}
由于
_Idx
是无符号的,而_Off
是带符号的,而_Off
的实际(文字)值为负,因此导致错误C4308。更简单的测试用例:
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;
}