I wrote a lightweight string_view
wrapper for a C++14 project, and with MSVC 2017 it is triggering a static_assert
at compile-time, yet the same code at run-time is passes the regular assert
. My question is, is this a compiler bug, manifest undefined behaviour, or something else entirely?
Here's the distilled code:
#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
}
Here's a Compiler Explorer link that I used to replicate the problem.
From what I can tell, adding or subtracting 0
from any pointer value is always valid:
- c++ - Is the behavior of subtracting two NULL pointers defined? - Stack Overflow, last blockquote
- Additive operators - cppreference.com, last bullet of the last bullet-list
- libstdc++: string_view Source File, implementation of
end()
etc.
Workaround:
If I change my end
method to the following, the failing static_assert
s will pass.
constexpr char const* end() const noexcept
{ return ( m_data == nullptr
? m_data
: m_data + m_size ); }
Tinkering:
I thought maybe the expression m_data + m_size
itself is UB, before the fact that m_size == 0
is evaluated. Yet, if I replace the implementation of end
with the nonsensical return m_data + 0;
, this still generates the two static_assert
errors. :-/
Update:
This does appear to be a compiler bug that was fixed between 15.7 and 15.8.