1. Changelog
-
R0
-
First submission
-
2. Motivation and Scope
[P1989R2] added a new constructor to
that made it implictly constructible from any compatible range (contiguous, sized, that uses the same character type as the string view, and so on; cf. [string.view.cons]/11).
[P2499R0] subsequently modified this constructor, by making it unconditionally
.
The reasoning for this change was that not all compatible ranges are convertible to a string view by using their own size (the "range size"). In many important use cases, the size that is meant to be used is the "
size" (intended as the distance of a given marker, usually
, from the beginning of the range). In those cases, having an implicit conversion towards string views may end up introducing serious bugs in a program.
For instance, [P2499R0] has this example featuring "Modern C++" datatypes:
extern void get_string ( std :: span < char > buffer ); extern void use_string ( std :: string_view str ); std :: array < char , 200 > buf ; get_string ( buf ); use_string ( buf ); // oops
This code used to compile before [P2499R0]. However: if the semantics of
is to NUL-terminate its output,
will instead create a view over the entire array, spanning over the embedded
. This opens the door to any sort of bug (incl. UB, by reading uninitialized data in the array), and could have security implications. To prevent this from happening, the conversion from
to
has been made explicit, and the example code above is now ill-formed.
This change has a few consequences.
First and foremost it’s worth underlying that the proposed solution is still somehow a band-aid for this sort of use cases. If one uses
as a storage type instead of some other contiguous container (like
), then the example above would still have the problem of using the "wrong" size when
is converted to a
. The conversion from
to
remains however implicit.
Second, and more in general; in a sense, the proposed change splits the types that can be converted to a string view in two families:
-
types for which we now axiomatically establish that their size is always the "range size", and these convert implictly (e.g.
);std :: string -
types for which don’t necessarily know which one is the "correct" size, and those must be converted explicitly after [P2499R0] (e.g.
).std :: vector < char >
The problem with split (and in particular with 2.) is that it fundamentally kills the usability of string views as an universal interface type for read-only non-owning string-like inputs.
For instance, if one has a string type different than
, then using it with a
becomes awkward:
void use ( std :: string_view v ); std :: string str1 ; use ( str1 ); // OK my :: string str2 ; use ( str2 ); // ERROR
Unless
itself provides an implicit conversion operator towards
, the code above is ill-formed, requiring an explicit conversion instead.
So, what’s the problem, just provide the operator, like
itself does? That’s not so simple: the user may not be in control of
to begin with. For instance,
may belong to a 3rd party library (Boost, Qt, folly, and so on); every sufficiently big codebase has its own string class(es).
This usability impedance is there also if one goes in the opposite direction. Suppose one has a
object, and wants to use it together with a 3rd party string library. That library also defines its own string view types, and following the Standard Library’s lead, these string view types feature explicit construction from compatible ranges.
Then:
void lib::very_useful_algorithm ( lib :: string_view v ); std :: string str ; lib :: very_useful_algorithm ( str ); // ERROR
There is absolutely no reason why the above shouldn’t compile.
and
are perfectly compatible, with
representing the same "platonic concept" of a non-owning
. It should be therefore be implictly convertible.
Alas, that’s not the case (due to explicit construction from a range), and since we cannot modify either datatype (both belong to 3rd parties), we must add explicit conversions everywhere. This is completely anti-ergonomic for users.
The idea of adding conversion constructors/operators also does not scale. Again assuming that any 3rd party string view implementation follows [P2499R0]'s design, should the author of
add implicit conversion operators towards
,
,
,
,
, ...? This is poor design (requires O(N²) conversion constructors/operators), and obviously it’s completely impossible to implement in practice (unless one controls all the classes in question).
In Qt, the author implemented the same semantic change of P2499R0 for Qt’s string view classes. The result was that certain implicit conversions got broken, such as from
to
, despite the two classes representing exactly the same concept (a view over UTF-16/
data).
What we are highlighting here is that there is tension between the safety concerns behind the changes proposed by [P2499R0]'s, and the usefulness of string views as a vocabulary interface type -- usefulness that gets dramatically reduced (or removed, one could say) by making the range conversion unconditionally explicit.
We could restore most of this usefulness by making the range conversion implicit in the cases where we know it’s safe to do. The only problematic aspect is: how to identify those cases?
3. Design Decisions
3.1. How to identify a string(view) class?
Unfortunately there isn’t an universal way to identify all and only string(view)-like classes for which we’d like to enable implicit conversions towards string views. As far as their range properties are concerned, a
and a
are identical -- both are contiguous, sized, etc.; a view class like
cannot distinguish between them based on those range properties alone.
We need therefore a trait, which necessarily needs to be opt-in. The point is that we want to err on the side of caution: for an arbitrary contiguous range we want to keep the conversion explicit. A string(view)-like class can enable the opt-in and become implicitly convertible instead, and string view classes such as
can be adapted to accept implicit conversions from compatible ranges that have the trait enabled.
There are two alternatives for the design of such a trait.
-
We can introduce a brand new type trait to specialize, and/or a class to inherit from, similar to various other precedents in the Standard Library (for instance,
). Any string(view)-like class can then enable the trait by specializing/inheriting.std :: ranges :: enable_view < T > -
We can use some "unique" feature of string(view)-like types. Specifically, this paper proposes to detect the presence and compatibility of an inner
type in order to classify a compatible range as a string(view)-like type. This idea was originally proposed as an option by [P2499R0] but ultimately not chosen; at the time of this writing we do not know the exact reasons.traits_type
Both of these options have some downsides.
-
In general, both options require an opt-in from existing code. However there is already significant production code that implements approach 2, like
,std :: string
,std :: string_view
,folly :: fbstring
,boost :: string_view
, etc.; while there is obviously no code that implements 1. These existing datatypes will see their compatibility restored "for free".absl :: string_view -
The inner
type is not necessarily specific to string(view)-like classes. From simply a naming point of view, it does not have the word "char" or "string" in it or anything like that. The risk here is that some classes might be using it with completely different semantics, and this could result in misdetections.traits_type It sounds pretty unlikely that a compatible range declares a compatible
with a string view, but the range in question is not a string(view)-like. The compatibility oftraits_type
is checked in the constraints at [string.view.cons]/12 (but cf. [LWG3857] the proposed wording). For reference, in the Standard,traits_type
is used bytraits_type
andbasic_string
; bybasic_string_view
; and by the hierarchy of classes in iostreams rooted inbasic_regex
. Of these, onlybasic_ios
andbasic_string
are of a compatible range type and with a compatible trait.basic_string_view Nonetheless, we sampled various projects in order to back this claim with some real-world data. The results are:
Project Findings Folly Only used in
, which has string semantics.fbstring LLVM (excluding libcxx and tests) Used by OpenMP in some flag types (
,kmp_flag
,kmp_flag_native
), which are not ranges.kmp_flag_atomic Abseil Only used in
, which has string view semantics (it’s a backport of C++17’sstring_view
).std :: string_view Firefox According to Searchfox, only 3rd party code (Abseil, protobuf) defines a
type (alias).traits_type Range-v3 Used in the ostream iterators like
.ostream_iterator Qt There are no occurrences of
in Qt.traits_type Boost Boost has a considerable number of usages, about 140 (a complete listing is in Appendix A: traits_type in Boost 1.81), the majority of which are not string related classes at all. This indeed shows that the
name is quite generic, and somehow gets used as as the "default/go-to name" for a type alias for a traits-like parameter of any sort.traits_type Still, there’s a non-trivial amount of string(view)-like classes. Adding transparent interoperability would be a major usability win. Amongst the string classes, these are compatible with
's range constructor and have a compatiblebasic_string_view
:traits_type -
In Container,
.basic_string -
In Core there’s an internal
(also used by Url).basic_string_view -
In Log, there’s an internal
.basic_string_literal -
In StaticString,
.basic_static_string -
In Process V2,
,basic_cstring_ref
,key_view
andvalue_view
.key_value_pair_view -
In Test,
.basic_cstring -
In Url,
.pct_string_view -
In Utility,
.string_view -
In Wave,
.flex_string
There’s also some non compatible string class:
-
In Compute,
definesbasic_string
, but it is not a contiguous range (it’s in GPU memory).traits_type
In the rest of usages, the containing datatype is not a range at all, and/or
is defining some datatype non compatible withtraits_type
anyways.basic_string_view In conclusion, these results strongly point towards the conclusion that only string(view)-like types are detected: the detection is correct. It is not complete: types such as
/QString
from Qt orQStringView
are string(view)-like types, but are missing a compatiblellvm :: StringRef
(specifically, they lacktraits_type
entirely).traits_type -
-
A downside of option 1 is the timing aspect. Adding a brand new trait would mean crippling string views for one additional C++ version cycle, until one can use a C++26 (?) compiler that adds the trait and one can use it. Option 2 could (and in our opinion should) be treated as a defect fix instead.
Given the results above, the author’s preference would be for option 2, but we would like to gather feedback from SG9, SG16 and LEWG.
3.2. Is this a defect fix?
If option 2 is chosen, we would prefer that the changes proposed by this paper would be considered as a defect fix by implementors and backported to their C++23 modes. For this reason we would not be proposing any new feature-testing macro, nor to bump the value of an existing one. (The change is still entirely detectable by users via type traits, should the need arise.)
Option 1 is fundamentally a brand new API, so it cannot be considered a fix.
4. Impact on the Standard
This proposal is a pure library change. Depending on the option chosen, it may extend/change the proposed resolution to [LWG3857].
The impact is strictly positive: code that was ill-formed (after [P2499R0]) becomes well-formed.
5. Technical Specifications
All the proposed changes are relative to [N4928].
6. Proposed wording
The wording is very similar for both options outlined above. Option 1 would add a new feature-testing macro.
6.1. Option 1: introduce a dedicated type trait
Add to the list in [version.syn]:
#define __cpp_lib_is_string_view_like YYYYMML // also in <string>, <string_view>
with the
replaced as usual (year and month of adoption of the present proposal).
Modify [string.view.synop] as follows:
// [string.view.template], class template basic_string_view template < class charT , class traits = char_traits < charT >> class basic_string_view ; template < class T > constexpr bool is_string_view_like = false; template < class charT , class traits > constexpr bool is_string_view_like < basic_string_view < charT , traits >> = true;
Add a new section after [string.view.literals]:
23.4 Enablers for implicit string view construction from ranges [string.view.range.construction]1. Remarks: Pursuant to [namespace.std], users may specializetemplate < class T > constexpr bool is_string_view_like = false; for cv-unqualified program-defined types. Such specializations shall be usable in constant expressions ([expr.const]) and have type
is_string_view_like .
const bool
Modify [string.syn] as follows:
// [basic.string], basic_string template < class charT , class traits = char_traits < charT > , class Allocator = allocator < charT >> class basic_string ; template < class charT , class traits , class Allocator > constexpr bool is_string_view_like < basic_string < charT , traits , Allocator >> = true;
Modify [string.view.template.general] as shown:
template < class R > constexpr explicit ( see below ) basic_string_view ( R && r );
Modify [string.view.cons] as shown:
template < class R > constexpr explicit ( see below ) basic_string_view ( R && r );
Let
be an lvalue of type
d .
remove_cvref_t < R > Constraints:
(12.1) —
is not the same type as
remove_cvref_t < R > ,
basic_string_view (12.2) —
models
R and
ranges :: contiguous_range ,
ranges :: sized_range (12.3) —
is
is_same_v < ranges :: range_value_t < R > , charT > true
,(12.4) —
is
is_convertible_v < R , const charT *> false
, and(12.5) —
is not a valid expression
d . operator :: std :: basic_string_view < charT , traits > () , and.(12.6) — if the qualified-idis valid and denotes a type,
remove_reference_t < R >:: traits_type is
is_same_v < remove_reference_t < R >:: traits_type , traits > true
.15. Remarks: If the qualified-id
Effects: Initializes
with
data_ and
ranges :: data ( r ) with
size_ .
ranges :: size ( r ) Throws: Any exception thrown by
and
ranges :: data ( r ) .
ranges :: size ( r ) is valid, denotes a type,
remove_reference_t < R >:: traits_type is
is_same_v < remove_reference_t < R >:: traits_type , traits > true
, andis
is_string_view_like < remove_reference_t < R >> true
, then the expression insideis equivalent to
explicit false
. Otherwise, it is equivalent totrue
.
6.2. Option 2: use a compatible inner type_traits
type
Modify [string.view.template.general] as shown:
template < class R > constexpr explicit ( see below ) basic_string_view ( R && r );
Modify [string.view.cons] as shown:
template < class R > constexpr explicit ( see below ) basic_string_view ( R && r );
Let
be an lvalue of type
d .
remove_cvref_t < R > Constraints:
(12.1) —
is not the same type as
remove_cvref_t < R > ,
basic_string_view (12.2) —
models
R and
ranges :: contiguous_range ,
ranges :: sized_range (12.3) —
is
is_same_v < ranges :: range_value_t < R > , charT > true
,(12.4) —
is
is_convertible_v < R , const charT *> false
, and(12.5) —
is not a valid expression
d . operator :: std :: basic_string_view < charT , traits > () , and.(12.6) — if the qualified-idis valid and denotes a type,
remove_reference_t < R >:: traits_type is
is_same_v < remove_reference_t < R >:: traits_type , traits > true
.15. Remarks: If the qualified-id
Effects: Initializes
with
data_ and
ranges :: data ( r ) with
size_ .
ranges :: size ( r ) Throws: Any exception thrown by
and
ranges :: data ( r ) .
ranges :: size ( r ) is valid, denotes a type, and
remove_reference_t < R >:: traits_type is
is_same_v < remove_reference_t < R >:: traits_type , traits > true
, then the expression insideis equivalent to
explicit false
. Otherwise, it is equivalent totrue
.
Appendix A: traits_type
in Boost 1.81
boost / asio / basic_deadline_timer . hpp : 148 : typedef TimeTraits traits_type ; boost / asio / basic_waitable_timer . hpp : 170 : typedef WaitTraits traits_type ; boost / asio / experimental / basic_channel . hpp : 137 : typedef typename Traits :: template rebind < Signatures ... >:: other traits_type ; boost / asio / experimental / basic_concurrent_channel . hpp : 137 : typedef typename Traits :: template rebind < Signatures ... >:: other traits_type ; boost / asio / experimental / detail / channel_service . hpp : 299 : typedef typename Traits :: template rebind < Signatures ... >:: other traits_type ; boost / asio / experimental / detail / channel_service . hpp : 396 : typedef typename Traits :: template rebind < R () >:: other traits_type ; boost / asio / experimental / detail / channel_service . hpp : 499 : traits_type ; boost / asio / experimental / detail / impl / channel_service . hpp : 196 : Signatures ... >:: traits_type traits_type ; boost / asio / experimental / detail / impl / channel_service . hpp : 224 : Signatures ... >:: traits_type traits_type ; boost / asio / experimental / detail / impl / channel_service . hpp : 260 : Signatures ... >:: traits_type traits_type ; boost / asio / experimental / detail / impl / channel_service . hpp : 546 : Signatures ... >:: traits_type traits_type ; boost / beast / core / detail / ostream . hpp : 122 : using traits_type = typename boost / beast / core / detail / ostream . hpp : 51 : using traits_type = typename boost / beast / core / detail / static_ostream . hpp : 30 : using traits_type = typename boost / compute / container / basic_string . hpp : 46 : typedef Traits traits_type ; boost / container / string . hpp : 618 : typedef Traits traits_type ; boost / context / fixedsize_stack . hpp : 44 : typedef traitsT traits_type ; boost / context / pooled_fixedsize_stack . hpp : 127 : typedef traitsT traits_type ; boost / context / posix / protected_fixedsize_stack . hpp : 46 : typedef traitsT traits_type ; boost / context / posix / segmented_stack . hpp : 46 : typedef traitsT traits_type ; boost / context / windows / protected_fixedsize_stack . hpp : 38 : typedef traitsT traits_type ; boost / core / detail / string_view . hpp : 350 : typedef std :: char_traits < Ch > traits_type ; boost / coroutine / posix / protected_stack_allocator . hpp : 42 : typedef traitsT traits_type ; boost / coroutine / posix / segmented_stack_allocator . hpp : 43 : typedef traitsT traits_type ; boost / coroutine / standard_stack_allocator . hpp : 41 : typedef traitsT traits_type ; boost / coroutine / windows / protected_stack_allocator . hpp : 35 : typedef traitsT traits_type ; boost / date_time / date . hpp : 62 : typedef typename calendar :: date_traits_type traits_type ; boost / date_time / date_parsing . hpp : 145 : typedef typename std :: basic_string < char >:: traits_type traits_type ; boost / date_time / date_parsing . hpp : 314 : typedef typename std :: basic_string < charT >:: traits_type traits_type ; boost / date_time / time_duration . hpp : 285 : typedef typename base_duration :: traits_type traits_type ; boost / date_time / time_duration . hpp : 50 : typedef rep_type traits_type ; boost / date_time / time_parsing . hpp : 57 : typedef typename std :: basic_string < char_type >:: traits_type traits_type ; boost / fiber / future / packaged_task . hpp : 58 : > traits_type ; boost / fiber / future / promise . hpp : 40 : typedef std :: allocator_traits < typename object_type :: allocator_type > traits_type ; boost / format / alt_sstream . hpp : 47 : typedef Tr traits_type ; boost / geometry / srs / projections / impl / pj_ell_set . hpp : 179 : typedef srs :: spar :: detail :: ellps_traits < param_type > traits_type ; boost / geometry / srs / projections / spar . hpp : 1004 : typedef proj_traits < proj_type > traits_type ; boost / geometry / srs / projections / spar . hpp : 1021 : typedef proj_traits < typename o_proj_type :: type > traits_type ; boost / graph / distributed / adjacency_list . hpp : 1308 : traits_type ; boost / graph / vertex_and_edge_range . hpp : 24 : typedef graph_traits < Graph > traits_type ; boost / histogram / ostream . hpp : 46 : using traits_type = typename OStream :: traits_type ; boost / interprocess / streams / bufferstream . hpp : 274 : typedef typename std :: basic_ios < char_type , CharTraits >:: traits_type traits_type ; boost / interprocess / streams / bufferstream . hpp : 345 : typedef typename std :: basic_ios < char_type , CharTraits >:: traits_type traits_type ; boost / interprocess / streams / bufferstream . hpp : 417 : typedef typename std :: basic_ios < char_type , CharTraits >:: traits_type traits_type ; boost / interprocess / streams / bufferstream . hpp : 72 : typedef CharTraits traits_type ; boost / interprocess / streams / vectorstream . hpp : 388 : typedef typename std :: basic_ios < char_type , CharTraits >:: traits_type traits_type ; boost / interprocess / streams / vectorstream . hpp : 469 : typedef typename std :: basic_ios < char_type , CharTraits >:: traits_type traits_type ; boost / interprocess / streams / vectorstream . hpp : 544 : typedef typename std :: basic_ios < char_type , CharTraits >:: traits_type traits_type ; boost / interprocess / streams / vectorstream . hpp : 76 : typedef CharTraits traits_type ; boost / io / ostream_joiner . hpp : 48 : typedef Traits traits_type ; boost / iostreams / chain . hpp : 422 : typedef Tr traits_type ; \boost / iostreams / chain . hpp : 450 : typedef typename chain_type :: traits_type traits_type ; boost / iostreams / detail / buffer . hpp : 87 : typedef iostreams :: char_traits < Ch > traits_type ; boost / iostreams / detail / double_object . hpp : 101 : typedef boost :: call_traits < T > traits_type ; boost / iostreams / detail / double_object . hpp : 36 : typedef Metrowerks :: call_traits < T > traits_type ; boost / iostreams / detail / double_object . hpp : 38 : typedef boost :: call_traits < T > traits_type ; boost / iostreams / detail / double_object . hpp : 59 : typedef Metrowerks :: call_traits < T > traits_type ; boost / iostreams / detail / double_object . hpp : 61 : typedef boost :: call_traits < T > traits_type ; boost / iostreams / detail / double_object . hpp : 99 : typedef Metrowerks :: call_traits < T > traits_type ; boost / iostreams / filter / grep . hpp : 50 : typedef char_traits < char_type > traits_type ; boost / iostreams / filter / gzip . hpp : 463 : typedef char_traits < char > traits_type ; boost / iostreams / filter / line . hpp : 46 : typedef char_traits < char_type > traits_type ; boost / iostreams / filter / stdio . hpp : 52 : typedef BOOST_IOSTREAMS_CHAR_TRAITS ( Ch ) traits_type ; boost / iostreams / filter / symmetric . hpp : 76 : typedef BOOST_IOSTREAMS_CHAR_TRAITS ( char_type ) traits_type ; boost / iostreams / invert . hpp : 53 : typedef char_traits < char_type > traits_type ; boost / iostreams / read . hpp : 120 : typedef BOOST_IOSTREAMS_CHAR_TRAITS ( char_type ) traits_type ; boost / iostreams / read . hpp : 133 : typedef iostreams :: char_traits < char_type > traits_type ; boost / iostreams / read . hpp : 157 : typedef iostreams :: char_traits < char_type > traits_type ; boost / iostreams / read . hpp : 169 : typedef iostreams :: char_traits < char_type > traits_type ; boost / iostreams / read . hpp : 222 : typedef iostreams :: char_traits < char_type > traits_type ; boost / iostreams / skip . hpp : 40 : typedef iostreams :: char_traits < char_type > traits_type ; boost / iostreams / stream . hpp : 31 : typedef Tr traits_type ; boost / iostreams / traits . hpp : 277 : > traits_type ; boost / iostreams / traits . hpp : 353 : typedef Tr traits_type ; \boost / iostreams / write . hpp : 81 : typedef BOOST_IOSTREAMS_CHAR_TRAITS ( char_type ) traits_type ; boost / iostreams / write . hpp : 98 : typedef BOOST_IOSTREAMS_CHAR_TRAITS ( char_type ) traits_type ; boost / lexical_cast / lexical_cast_old . hpp : 98 : typedef Traits traits_type ; boost / log / detail / attachable_sstream_buf . hpp : 61 : typedef typename base_type :: traits_type traits_type ; boost / log / expressions / formatter . hpp : 59 : typedef typename StreamT :: traits_type traits_type ; boost / log / expressions / formatters / c_decorator . hpp : 134 : typedef c_decorator_traits < char_type > traits_type ; boost / log / expressions / formatters / c_decorator . hpp : 194 : typedef aux :: c_decorator_traits < char_type > traits_type ; boost / log / expressions / formatters / csv_decorator . hpp : 93 : typedef csv_decorator_traits < char_type > traits_type ; boost / log / expressions / formatters / xml_decorator . hpp : 92 : typedef xml_decorator_traits < char_type > traits_type ; boost / log / sources / record_ostream . hpp : 98 : typedef typename base_type :: traits_type traits_type ; boost / log / support / date_time . hpp : 70 : typedef typename TimeDurationT :: traits_type traits_type ; boost / log / utility / formatting_ostream . hpp : 127 : typedef TraitsT traits_type ; boost / log / utility / string_literal . hpp : 58 : typedef TraitsT traits_type ; boost / numeric / interval / interval . hpp : 45 : typedef Policies traits_type ; boost / polygon / polygon_90_set_traits . hpp : 56 : typedef typename traits_by_concept < T , T2 >:: type traits_type ; boost / polygon / polygon_90_set_traits . hpp : 62 : typedef typename traits_by_concept < T , T2 >:: type traits_type ; boost / process / detail / posix / basic_pipe . hpp : 34 : typedef Traits traits_type ; boost / process / detail / windows / basic_pipe . hpp : 31 : typedef Traits traits_type ; boost / process / pipe . hpp : 105 : typedef Traits traits_type ; boost / process / pipe . hpp : 306 : typedef Traits traits_type ; boost / process / pipe . hpp : 40 : typedef Traits traits_type ; boost / process / pipe . hpp : 417 : typedef Traits traits_type ; boost / process / pipe . hpp : 526 : typedef Traits traits_type ; boost / process / v2 / cstring_ref . hpp : 55 : using traits_type = Traits ; boost / process / v2 / environment . hpp : 117 : using traits_type = key_char_traits < char_type > ; boost / process / v2 / environment . hpp : 208 : using traits_type = value_char_traits < char_type > ; boost / process / v2 / environment . hpp : 303 : using traits_type = std :: char_traits < char_type > ; boost / process / v2 / environment . hpp : 487 : using traits_type = key_char_traits < char_type > ; boost / process / v2 / environment . hpp : 707 : using traits_type = value_char_traits < char_type > ; boost / process / v2 / environment . hpp : 934 : using traits_type = std :: char_traits < char_type > ; boost / range / detail / collection_traits_detail . hpp : 288 : typedef array_traits < T > traits_type ; boost / regex / concepts . hpp : 301 : typedef typename regex_traits_computer < Regex >:: type traits_type ; boost / regex / v4 / basic_regex . hpp : 333 : typedef traits traits_type ; boost / regex / v4 / match_results . hpp : 384 : typedef :: boost :: regex_traits_wrapper < typename RegexT :: traits_type > traits_type ; boost / regex / v4 / match_results . hpp : 396 : typedef :: boost :: regex_traits_wrapper < typename RegexT :: traits_type > traits_type ; boost / regex / v5 / basic_regex . hpp : 322 : typedef traits traits_type ; boost / regex / v5 / match_results . hpp : 355 : typedef :: boost :: regex_traits_wrapper < typename RegexT :: traits_type > traits_type ; boost / regex / v5 / match_results . hpp : 367 : typedef :: boost :: regex_traits_wrapper < typename RegexT :: traits_type > traits_type ; boost / spirit / home / karma / stream / ostream_iterator . hpp : 36 : typedef Traits traits_type ; boost / static_string / static_string . hpp : 833 : using traits_type = Traits ; boost / test / utils / basic_cstring / basic_cstring . hpp : 50 : typedef ut_detail :: bcs_char_traits < CharT > traits_type ; boost / test / utils / basic_cstring / basic_cstring . hpp : 622 : typedef typename basic_cstring < CharT1 >:: traits_type traits_type ; boost / test / utils / basic_cstring / compare . hpp : 105 : typedef typename boost :: unit_test :: basic_cstring < CharT >:: traits_type traits_type ; boost / test / utils / nullstream . hpp : 46 : typedef typename base_type :: traits_type traits_type ; boost / url / grammar / string_view_base . hpp : 86 : typedef std :: char_traits < char > traits_type ; boost / utility / string_view . hpp : 58 : typedef traits traits_type ; boost / wave / util / flex_string . hpp : 1445 : typedef T traits_type ; boost / wave / util / flex_string . hpp : 2381 : typedef typename flex_string < E , T , A , S >:: traits_type traits_type ; boost / xpressive / detail / core / linker . hpp : 117 : typedef typename regex_traits_type < Locale , BidiIter >:: type traits_type ; boost / xpressive / detail / core / matcher / charset_matcher . hpp : 31 : typedef Traits traits_type ; boost / xpressive / detail / core / matcher / posix_charset_matcher . hpp : 33 : typedef Traits traits_type ; boost / xpressive / detail / static / compile . hpp : 77 : typedef typename default_regex_traits < char_type >:: type traits_type ; boost / xpressive / detail / static / compile . hpp : 90 : typedef typename regex_traits_type < locale_type , BidiIter >:: type traits_type ; boost / xpressive / detail / static / transforms / as_set . hpp : 107 : typedef typename Data :: traits_type traits_type ; boost / xpressive / detail / static / visitor . hpp : 108 : typedef Traits traits_type ; boost / xpressive / detail / utility / boyer_moore . hpp : 42 : typedef Traits traits_type ; boost / xpressive / detail / utility / chset / chset . hpp : 34 : typedef Traits traits_type ; boost / xpressive / regex_compiler . hpp : 59 : typedef RegexTraits traits_type ;
7. Acknowledgements
Thanks to KDAB for supporting this work.
All remaining errors are ours and ours only.