Document number: P2166R0
Project: Programming Language C++
Audience: LEWG-I, LEWG, LWG
Yuriy Chernyshov <georgthegreat@gmail.com>, <thegeorg@yandex-team.ru>
Date: 2020-05-06
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.
Existing implementations (i. e. libc++) might add a runtime assertion to forbid such behavior. Certain OpenSource projects would trigger this assertion. The list includes, but not limited to:
On a large private monorepo applying proposed changes and running an automatic CI-check helped to find 7 problematic projects (the number includes projects listed above), one of which would actually segfault if the code was reached (and the code was really easy reachable).
This proposal attempts to improve the diagnostics by explicitly deleting the problematic constructors, thus moving these assertions to compile time.
This proposal changes <string>
and <string_view>
headers only and does not affect the language core.
The wording is relative to N4861.
[...]
namespace std {
template<class charT, class traits = char_traits<charT>,
class Allocator = allocator<charT>>
class basic_string {
public:
// types
[...]
// [string.cons], construct/copy/destroy
[...]
constexpr basic_string(const charT* s, size_type n, const Allocator& a = Allocator());
constexpr basic_string(const charT* s, const Allocator& a = Allocator());
+ constexpr basic_string(nullptr_t) = delete;
[...]
template<class T>
constexpr basic_string& operator=(const T& t);
constexpr basic_string& operator=(const charT* s);
+ constexpr basic_string& operator=(nullptr_t) = delete;
[...]
};
[...]
}
[...]
template<class charT, class traits = char_traits<charT>>
class basic_string_view {
public:
// types
[...]
constexpr basic_string_view(const charT* str);
+ constexpr basic_string_view(nullptr_t) = delete;
[...]
};
These changes would not allow to remove runtime check, the following code will remain compilable and will trigger the assertion:
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.
The author would like to thank Antony Poloukhin, Marshall Clow and Eric Fiselier for a thorough review and suggestions.