C++ Logo

std-proposals

Advanced search

Re: [std-proposals] [Idea] Null-terminated string view (c_str_view / c_wstr_view) – a simpler alternative/complement to zstring_view

From: FPE <foxofice.fpe_at_[hidden]>
Date: Sun, 1 Feb 2026 17:18:58 +0800
>
> Thank you all for the detailed and quick feedback! I've thought a lot
>> about the concerns raised, especially around exceptions in views and
>> performance/preconditions.
>>
>> To address the main objection (runtime cost of exceptions/checks), I've
>> revised the design: checks and throws are now *only* in debug builds
>> (#ifndef NDEBUG), similar to how many libraries use asserts for
>> preconditions. In release builds, it's zero-overhead with precondition
>> violations being UB – aligning more closely with string_view/span
>> philosophy.
>>
>> Revised code:
>>
>> #include <string_view>
>> #include <string>
>> #include <stdexcept>
>>
>> namespace std
>> {
>>
>> template <typename T>
>> class basic_c_str_view
>> {
>> public:
>> // Constructor
>> inline basic_c_str_view() noexcept
>> : m_data(m_s_empty_buffer), m_size(0) {}
>> //--------------------------------------------------
>> inline basic_c_str_view(const T *txt)
>> {
>> #ifndef NDEBUG
>> if(txt == nullptr) [[unlikely]]
>> throw runtime_error("txt must not be nullptr");
>> #endif // !NDEBUG
>>
>> m_data = txt;
>> m_size = char_traits<T>::length(txt);
>> }
>> //--------------------------------------------------
>> inline basic_c_str_view(const T *txt, size_t size)
>> {
>> #ifndef NDEBUG
>> if(txt[size] != (T)'\0') [[unlikely]]
>> throw runtime_error("txt is not null-terminated");
>> #endif // !NDEBUG
>>
>> m_data = txt;
>> m_size = size;
>> }
>> //--------------------------------------------------
>> inline basic_c_str_view(basic_string_view<T> sv)
>> {
>> #ifndef NDEBUG
>> if(sv.data()[sv.size()] != (T)'\0') [[unlikely]]
>> throw runtime_error("sv is not null-terminated");
>> #endif // !NDEBUG
>>
>> m_data = sv.data();
>> m_size = sv.size();
>> }
>> //--------------------------------------------------
>> inline basic_c_str_view(const basic_string<T> &str) noexcept
>> : m_data(str.data()), m_size(str.size()) {}
>>
>> inline bool empty() const noexcept { return m_size == 0; }
>> inline const T* c_str() const noexcept { return m_data; }
>> inline size_t size() const noexcept { return m_size; }
>> inline basic_string_view<T> to_string_view() noexcept { return { m_data,
>> m_size }; }
>>
>> protected:
>> const T *m_data;
>> size_t m_size;
>>
>> private:
>> static inline const T m_s_empty_buffer[1] = { (T)'\0' };
>> };
>>
>> using c_str_view = basic_c_str_view<char>;
>> using c_wstr_view = basic_c_str_view<wchar_t>;
>>
>> } // namespace std
>>
>>
>> This keeps debug-time safety (fail-fast on bad input) while preserving
>> release performance.
>>
>> Questions:
>> 1. Does this debug-only checking reduce the concerns about
>> exceptions/performance?
>> 2. Is relying on NDEBUG acceptable, or should we use something else
>> (e.g., contracts if available)?
>> 3. Still interested in merging ideas into P3655? (Happy to contribute
>> naming, string ctor, etc.)
>>
>> Thanks again!
>>
>> Best,
>> FPE(foxofice)
>
>

Received on 2026-02-01 09:19:11