【问题标题】:std::prev on std::array in a constexpr environmentconstexpr 环境中 std::array 上的 std::prev
【发布时间】:2019-01-31 02:44:54
【问题描述】:

我目前正在使用 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 编辑: MSVC Pre 2018 with /std:c++17 works) 下的编译器资源管理器中编译良好(没有测试其他)。

我真的没有想法。当我把它放在 main 方法中时,相同的代码会编译,所以这似乎是 constexpr 范围的问题。

【问题讨论】:

  • 看起来像 MSVC 中的一个简单错误。应该可以在 C++17 模式下编译。
  • C4146 和 C4308 对我来说是相同版本的警告
  • @SergeyA 嗯,您似乎使用了错误的编译器标志(请参阅最后的输出语句)。对于 MSVC,它是 /std:c++17。不过没关系,因为我在帖子中提到的版本不知道参数(?)。它仍然认为我们在 c++11 中,不允许非文字类型作为 constexpr 函数返回类型。
  • 我使用 /std:c++latest 编译,代码编译,test 指向 3。您显示的两个错误对我来说是警告,但代码确实编译。
  • @JulianWiesler 我指的是代码,而不是编译器。贴出的代码是有效的C++17代码,如果MSVC编译失败,就是MSVC的bug。

标签: c++ visual-c++ compiler-errors c++17 constexpr


【解决方案1】:

复制

我可以使用 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;
}

【讨论】:

  • 这有帮助,谢谢。但是为什么他们只打印这个错误行呢?在普通模板代码中,您至少会获得实际模板代码的伪堆栈跟踪,这会导致错误。也可以在 constexpr 中使用。
猜你喜欢
  • 1970-01-01
  • 2020-03-22
  • 1970-01-01
  • 2016-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-25
  • 1970-01-01
相关资源
最近更新 更多