c++language-lawyerc++23

Is string_view{ nullptr, 0 } valid?


Is this a valid C++ program?

#include <string_view>

int main() {
    auto _ = std::string_view{ nullptr, 0 };
}

Specifically, is the expression string_view{ nullptr, 0 } well-defined? The specification for this constructor under [string.view.cons] reads:

Preconditions: [str, str + len) is a valid range.

With that, another way of phrasing this question would be: Is [nullptr, nullptr) considered a "valid range" in C++?


I am confused mostly due to the wording used in P2166. It proposes rejecting the construction of a string_view from a nullptr alone. This is well justified. I am, however, failing to comprehend the section titled "Further Discourse" that explains why the "sized constructor counterparts" were excluded:

As a development of the above proposal it seems logical to remove sized counterpart of nullptr constructors, as the behavior is undefined if [s, s + count) is not a valid range (citation source is the same). That is, the following statements are suggested where appropriate:

basic_string(nullptr_t, size_t) == delete;
constexpr basic_string_view(nullptr_t, size_t) == delete;

These changes will break the legal, yet not legitimate case of constructing std::string using basic_string(nullptr, 0); and std::string_view using basic_string_view(nullptr, 0); and thus they were not included into the main text of the proposal.

Bonus questions:


Clarification: I'm not constructing a string_view from the nullptr/0 literals, i.e., I'm not trying to evade or obfuscate a default constructor call. The actual use case is closer to this:

#include <string_view>

struct X {
    operator char const*() const { return nullptr; }
private:
    friend size_t len(X const&) { return 0; }
};

int main() {
    X const x{};
    auto _ = std::string_view{ x, len(x) };
}

Solution

  • LWG has explicitly made it so it is

    I am confused mostly due to the wording used in P2166

    This is because P2166 is wrong (or possibly just reading an outdated website).

    Defect report 2235 was specifically about this, and mentions:

    relative to N3485, 27.4.3.3 [string.cons]/6 says "Requires: s shall not be a null pointer and n < npos"

    So at some point the standard did explicitly say that s should not be nullptr. LWG issue 3111 was a revision of a response to this defect which provided the current wording:

    Requires: [s, s + n) is a valid range

    No mention of nullptr. The linked issue is very explicit that the intention is that {nullptr, 0} should be OK, in fact it says that it's essential:

    This implies that passing a null pointer and a zero size to this constructor is violating the precondition, as null pointer cannot be described as "pointing to an array of at least n elements of charT". On the other hand, being able to pass {nullptr, 0} is essential for basic_string to be able to inter-operate with other containers that are allowed to use the null pointer value to represent sequences of size zero

    P2166R1 is very clear about where it's getting it's information from:

    According to the C++ Standard, the behavior of std::basic_string::basic_string(const CharT* s) constructor is undefined if [s, s + Traits::length(s)) is not a valid range (for example, if s is a null pointer) (citation is taken from cppreference.com, the standard have slighty different wording in 21.3.2.2 [string.cons]). Same applies to std::basic_string_view::basic_string_view(const CharT* s) constructor.

    So it's not actually reading the standard, it's reading cppreference.com. It's worth pointing out that P2166 is citing this wrt the plain const CharT* constructor here (no size argument) but apparently also cites cppreference when talking about the sized constructor:

    As a development of the above proposal it seems logical to remove sized counterpart of nullptr constructors, as the behavior is undefined if [s, s + count) is not a valid range (citation source is the same).

    Looking at the bottom of the linked page now, you can see both LWG issue 3111 listed as a DR, but also an explicit footnote disputing what P21166R1 is claiming!

    [nullptr, nullptr + 0) is a valid empty range, because adding zero to a null pointer value is also well-defined (the result is still a null pointer value)

    So basically you can entirely disregard P2166 because it seems to be operating under incorrect information. The standard body has been very clear that std::string_view{ nullptr, 0 } is and should be valid.