Date: Sun, 1 Feb 2026 10:59:28 +0800
Hello everyone,
I would like to propose a null-terminated string view type for the standard
library to ease interoperability with C APIs and WinAPI, where
std::string_view does not guarantee null-termination.
Motivation
----------
std::basic_string_view does not guarantee null-termination, which makes
direct use with C functions and WinAPI either unsafe (risk of buffer
overrun) or forces unnecessary copies to std::string. A dedicated view type
that *guarantees* null-termination would eliminate this friction without
sacrificing performance.
There is already an active proposal P3655R2 “std::zstring_view” that
addresses exactly this problem. However, I believe the design below offers
several advantages that make it worth considering either as a complement or
as a simpler alternative.
Proposed design
---------------
#include <string_view>
#include <string>
#include <stdexcept>
namespace std
{
template <typename T>
class basic_c_str_view
{
public:
// Constructor
inline basic_c_str_view()
{
m_data = m_s_empty_buffer;
m_size = 0;
}
//--------------------------------------------------
inline basic_c_str_view(const T *txt)
{
if(txt == nullptr) [[unlikely]]
throw runtime_error("txt must not be nullptr");
m_data = txt;
m_size = char_traits<T>::length(txt);
}
//--------------------------------------------------
inline basic_c_str_view(const T *txt, size_t size)
{
if(txt[size] != (T)'\0') [[unlikely]]
throw runtime_error("txt is not null-terminated");
m_data = txt;
m_size = size;
}
//--------------------------------------------------
inline basic_c_str_view(basic_string_view<T> sv)
{
if(sv.data()[sv.size()] != (T)'\0') [[unlikely]]
throw runtime_error("sv is not null-terminated");
m_data = sv.data();
m_size = sv.size();
}
//--------------------------------------------------
inline basic_c_str_view(const basic_string<T> &str)
{
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
Advantages over P3655R2 “std::zstring_view”
-------------------------------------------
1. Simpler, more minimal implementation – only two data members plus a
static empty buffer.
2. Extremely low implementation burden – essentially just a thin wrapper
with a few constructors and accessors.
3. Direct, zero-cost construction from std::string – no runtime check
needed because std::string always guarantees null-termination.
4. Safe default constructor that points to a static null-terminated empty
buffer (never nullptr).
5. Runtime checks throw exceptions on violation – this follows modern
engineering best practices: fail fast and visibly in debug builds rather
than invoke undefined behavior.
6. Seamless round-trip with std::string_view via to_string_view() – users
can freely move between the null-terminated-guarantee view and the general
view with minimal code.
7. Clearer, more intuitive naming – “c_str_view” immediately conveys
“C-style string view” and matches the familiar .c_str() member function;
“zstring_view” is less obvious to many developers.
8. Explicit wchar_t support with c_wstr_view – very helpful for Windows
developers who frequently need null-terminated wide strings.
9. Easy to teach and understand – the type does exactly what its name
suggests with no surprising corner cases.
10. No need for special “z” prefix that might confuse newcomers; aligns
better with existing standard library naming conventions (e.g., c_str(),
string_view).
I believe these qualities make the type easier to adopt, easier to
implement correctly, and less controversial to add to the standard.
Questions for the community
---------------------------
1. Is this design worth pursuing separately, or should effort be focused on
improving P3655 (zstring_view)?
2. Are there any fatal flaws in the exception-throwing approach? (I chose
exceptions for safety, but could be convinced to switch to precondition
assertions or UB if the committee prefers.)
3. Naming preferences: c_str_view, ntbs_view, something else?
4. Any other suggested changes or additional features?
I’m happy to revise the design based on feedback and, if the consensus is
to merge ideas, contribute to the existing zstring_view effort.
Thank you for your time and any comments!
Best regards,
FPE(foxofice)
foxofice.fpe_at_[hidden]
I would like to propose a null-terminated string view type for the standard
library to ease interoperability with C APIs and WinAPI, where
std::string_view does not guarantee null-termination.
Motivation
----------
std::basic_string_view does not guarantee null-termination, which makes
direct use with C functions and WinAPI either unsafe (risk of buffer
overrun) or forces unnecessary copies to std::string. A dedicated view type
that *guarantees* null-termination would eliminate this friction without
sacrificing performance.
There is already an active proposal P3655R2 “std::zstring_view” that
addresses exactly this problem. However, I believe the design below offers
several advantages that make it worth considering either as a complement or
as a simpler alternative.
Proposed design
---------------
#include <string_view>
#include <string>
#include <stdexcept>
namespace std
{
template <typename T>
class basic_c_str_view
{
public:
// Constructor
inline basic_c_str_view()
{
m_data = m_s_empty_buffer;
m_size = 0;
}
//--------------------------------------------------
inline basic_c_str_view(const T *txt)
{
if(txt == nullptr) [[unlikely]]
throw runtime_error("txt must not be nullptr");
m_data = txt;
m_size = char_traits<T>::length(txt);
}
//--------------------------------------------------
inline basic_c_str_view(const T *txt, size_t size)
{
if(txt[size] != (T)'\0') [[unlikely]]
throw runtime_error("txt is not null-terminated");
m_data = txt;
m_size = size;
}
//--------------------------------------------------
inline basic_c_str_view(basic_string_view<T> sv)
{
if(sv.data()[sv.size()] != (T)'\0') [[unlikely]]
throw runtime_error("sv is not null-terminated");
m_data = sv.data();
m_size = sv.size();
}
//--------------------------------------------------
inline basic_c_str_view(const basic_string<T> &str)
{
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
Advantages over P3655R2 “std::zstring_view”
-------------------------------------------
1. Simpler, more minimal implementation – only two data members plus a
static empty buffer.
2. Extremely low implementation burden – essentially just a thin wrapper
with a few constructors and accessors.
3. Direct, zero-cost construction from std::string – no runtime check
needed because std::string always guarantees null-termination.
4. Safe default constructor that points to a static null-terminated empty
buffer (never nullptr).
5. Runtime checks throw exceptions on violation – this follows modern
engineering best practices: fail fast and visibly in debug builds rather
than invoke undefined behavior.
6. Seamless round-trip with std::string_view via to_string_view() – users
can freely move between the null-terminated-guarantee view and the general
view with minimal code.
7. Clearer, more intuitive naming – “c_str_view” immediately conveys
“C-style string view” and matches the familiar .c_str() member function;
“zstring_view” is less obvious to many developers.
8. Explicit wchar_t support with c_wstr_view – very helpful for Windows
developers who frequently need null-terminated wide strings.
9. Easy to teach and understand – the type does exactly what its name
suggests with no surprising corner cases.
10. No need for special “z” prefix that might confuse newcomers; aligns
better with existing standard library naming conventions (e.g., c_str(),
string_view).
I believe these qualities make the type easier to adopt, easier to
implement correctly, and less controversial to add to the standard.
Questions for the community
---------------------------
1. Is this design worth pursuing separately, or should effort be focused on
improving P3655 (zstring_view)?
2. Are there any fatal flaws in the exception-throwing approach? (I chose
exceptions for safety, but could be convinced to switch to precondition
assertions or UB if the committee prefers.)
3. Naming preferences: c_str_view, ntbs_view, something else?
4. Any other suggested changes or additional features?
I’m happy to revise the design based on feedback and, if the consensus is
to merge ideas, contribute to the existing zstring_view effort.
Thank you for your time and any comments!
Best regards,
FPE(foxofice)
foxofice.fpe_at_[hidden]
Received on 2026-02-01 02:59:41
