Cha*_*cox 24 c++ undefined-behavior language-lawyer compiler-bug c++14
我string_view为C++ 14项目编写了一个轻量级的包装器,并且在MSVC 2017中它static_assert在编译时触发,但是在运行时相同的代码传递了常规assert.我的问题是,这是一个编译器错误,显示未定义的行为,还是完全不同的东西?
这是蒸馏代码:
#include <cassert> // assert
#include <cstddef> // size_t
class String_View
{
char const* m_data;
std::size_t m_size;
public:
constexpr String_View()
: m_data( nullptr ),
m_size( 0u )
{}
constexpr char const* begin() const noexcept
{ return m_data; }
constexpr char const* end() const noexcept
{ return m_data + m_size; }
};
void static_foo()
{
constexpr String_View sv;
// static_assert( sv.begin() == sv.end() ); // this errors
static_assert( sv.begin() == nullptr );
// static_assert( sv.end() == nullptr ); // this errors
}
void dynamic_foo()
{
String_View const sv;
assert( sv.begin() == sv.end() ); // this compiles & is optimized away
assert( sv.begin() == nullptr );
assert( sv.end() == nullptr ); // this compiles & is optimized away
}
Run Code Online (Sandbox Code Playgroud)
这是我用来复制问题的编译器资源管理器链接.
据我所知,0从任何指针值中添加或减去始终有效:
end()等.解决方法:
如果我将我的end方法更改为以下内容,则失败的static_asserts将通过.
constexpr char const* end() const noexcept
{ return ( m_data == nullptr
? m_data
: m_data + m_size ); }
Run Code Online (Sandbox Code Playgroud)
修修补补:
我想也许表达m_data + m_size本身就是UB,在m_size == 0被评估的事实之前.然而,如果我用end无意义的替换实现return m_data + 0;,这仍然会产生两个static_assert错误.: - /
更新:
这似乎是一个在15.7和15.8之间修复的编译器错误.
Sha*_*our 13
这看起来像一个MSVC错误,C++ 14草案标准明确允许0从[expr.add] p7向比较等于自身的指针添加和减去该值:
如果将值0添加到指针值或从指针值中减去,则结果将等于原始指针值.如果两个指针指向同一个对象,或者两个指针都指向同一个数组的末尾或两者都为空,并且减去了两个指针,则结果将比较等于转换为类型std :: ptrdiff_t的值0.
它看起来像CWG缺陷1776导致p0137调整[expr.add] p7明确说null pointer.
最新的草案使这更加明确[expr.add] p4:
当具有整数类型的表达式J被添加到指针类型的表达式P或从指针类型的表达式P中减去时,结果具有类型P.-
如果P求值为空指针值并且J求值为0,则结果为空指针值.
-否则,如果P指向元素x [I]与n个元素的数组对象x,85个表达P + j和j + P(其中J的值为j)的指向(可能-假设的)的元素x [ i + j]如果0≤i+j≤n,则表达式P-J指向(可能是假设的)元素x [i-j],如果0≤i-j≤n.(4.3).
- 否则,行为未定义.
这个改变是在编辑上看到这个github问题和这个PR.
MSVC在这里是不一致的,因为它允许在常量表达式中添加和减去零,就像gcc和clang一样.这是关键,因为常量表达式中的未定义行为是不正确的,因此需要诊断.鉴于以下内容:
constexpr int *p = nullptr ;
constexpr int z = 0 ;
constexpr int *q1 = p + z;
constexpr int *q2 = p - z;
Run Code Online (Sandbox Code Playgroud)
GCC,铛和MSVC允许它的常量表达式(活godbolt例子)虽然可悲MSVC是因为它允许非零值,以及,给出下面的双重不一致:
constexpr int *p = nullptr ;
constexpr int z = 1 ;
constexpr int *q1 = p + z;
constexpr int *q2 = p - z;
Run Code Online (Sandbox Code Playgroud)
clang和gcc都表示它是不正确的,而MSVC则没有(活神开玩笑).
| 归档时间: |
|
| 查看次数: |
1502 次 |
| 最近记录: |