I notice that in my copy of the SGI STL reference, there is a page about Character Traits but I can't see how these are used? Do they replace the string.h functions? They don't seem to be used by std::string
, e.g. the length()
method on std::string
doesn't make use of the Character Traits length()
method. Why do Character Traits exist and are they ever used in practice?
相关问题
- Sorting 3 numbers without branching [closed]
- How to compile C++ code in GDB?
- Why does const allow implicit conversion of refere
- how to split a list into a given number of sub-lis
- thread_local variables initialization
相关文章
- JSP String formatting Truncate
- Class layout in C++: Why are members sometimes ord
- How to mock methods return object with deleted cop
- Which is the best way to multiply a large and spar
- C++ default constructor does not initialize pointe
- Selecting only the first few characters in a strin
- What exactly do pointers store? (C++)
- Converting glm::lookat matrix to quaternion and ba
Character traits are an extremely important component of the streams and strings libraries because they allow the stream/string classes to separate out the logic of what characters are being stored from the logic of what manipulations should be performed on those characters.
To begin with, the default character traits class,
char_traits<T>
, is used extensively in the C++ standard. For example, there is no class calledstd::string
. Rather, there's a class templatestd::basic_string
that looks like this:Then,
std::string
is defined asSimilarly, the standard streams are defined as
So why are these classes structured as they are? Why should we be using a weird traits class as a template argument?
The reason is that in some cases we might want to have a string just like
std::string
, but with some slightly different properties. One classic example of this is if you want to store strings in a way that ignores case. For example, I might want to make a string calledCaseInsensitiveString
such that I can haveThat is, I can have a string where two strings differing only in their case sensitivity are compared equal.
Now, suppose that the standard library authors designed strings without using traits. This would mean that I'd have in the standard library an immensely powerful string class that was entirely useless in my situation. I couldn't reuse much of the code for this string class, since comparisons would always work against how I wanted them to work. But by using traits, it's actually possible to reuse the code that drives
std::string
to get a case-insensitive string.If you pull up a copy of the C++ ISO standard and look at the definition of how the string's comparison operators work, you'll see that they're all defined in terms of the
compare
function. This function is in turn defined by callingwhere
str
is the string you're comparing to andrlen
is the smaller of the two string lengths. This is actually quite interesting, because it means that the definition ofcompare
directly uses thecompare
function exported by the traits type specified as a template parameter! Consequently, if we define a new traits class, then definecompare
so that it compares characters case-insensitively, we can build a string class that behaves just likestd::string
, but treats things case-insensitively!Here's an example. We inherit from
std::char_traits<char>
to get the default behavior for all the functions we don't write:(Notice I've also defined
eq
andlt
here, which compare characters for equality and less-than, respectively, and then definedcompare
in terms of this function).Now that we have this traits class, we can define
CaseInsensitiveString
trivially asAnd voila! We now have a string that treats everything case-insensitively!
Of course, there are other reasons besides this for using traits. For example, if you want to define a string that uses some underlying character type of a fixed-size, then you can specialize
char_traits
on that type and then make strings from that type. In the Windows API, for example, there's a typeTCHAR
that is either a narrow or wide character depending on what macros you set during preprocessing. You can then make strings out ofTCHAR
s by writingAnd now you have a string of
TCHAR
s.In all of these examples, notice that we just defined some traits class (or used one that already existed) as a parameter to some template type in order to get a string for that type. The whole point of this is that the
basic_string
author just needs to specify how to use the traits and we magically can make them use our traits rather than the default to get strings that have some nuance or quirk not part of the default string type.Hope this helps!
EDIT: As @phooji pointed out, this notion of traits is not just used by the STL, nor is it specific to C++. As a completely shameless self-promotion, a while back I wrote an implementation of a ternary search tree (a type of radix tree described here) that uses traits to store strings of any type and using whatever comparison type the client wants them to store. It might be an interesting read if you want to see an example of where this is used in practice.
EDIT: In response to your claim that
std::string
doesn't usetraits::length
, it turns out that it does in a few places. Most notably, when you construct astd::string
out of achar*
C-style string, the new length of the string is derived by callingtraits::length
on that string. It seems thattraits::length
is used mostly to deal with C-style sequences of characters, which are the "least common denominator" of strings in C++, whilestd::string
is used to work with strings of arbitrary contents.