It should be noted that the language designers were aware of cctype
's tolower
when locale
's tolower
was created. It improved in 2 primary ways:
- As is mentioned in progressive_overload's answer the
locale
version allowed the use of the facet ctype
, even a user modified one, without requiring the shuffling in of a new LC_CTYPE
in via setlocale
and the restoration of the previous LC_CTYPE
- From section 7.1.6.2[dcl.type.simple]3:
It is implementation-defined whether objects of char
type are represented as signed or unsigned quantities. The signed
specifier forces char
objects to be signed
Which creates an the potential for undefined behavior with the cctype
version of tolower
's if it's argument:
Is not representable as unsigned char
and does not equal EOF
So there is an additional input and output static_cast
required by the cctype
version of tolower
yielding:
transform(cbegin(foo), cend(foo), begin(foo), [](const unsigned char i){ return tolower(i); });
Since the locale
version operates directly on char
s there is no need for a type conversion.
So if you don't need to perform the conversion in a different facet ctype
it simply becomes a style question of whether you prefer the transform
with a lambda required by the cctype
version, or whether you prefer the locale
version's:
use_facet<ctype<char>>(cout.getloc()).tolower(data(foo), next(data(foo), size(foo)));
static_cast
? Just dostd::transform(foo.cbegin(), foo.cend(), foo.begin(), ::tolower)
. Alternatively, consider boost'sto_lower
. – Intervalometer::tolower
is implementation dependent, and I always try to avoid Boost. – Steento_lower
is shorter, more readable, and has the option to pass in a locale as well. – Intervalometer::tolower
works fine with#include <ctype.h>
- it's all about choices (I'd personally rather put these few functions in the global namespace than to have to deal with overload disambiguation). And about boost : many people want to avoid it as much as possible - I learned to embrace it, but to each their own. I prefer the readability advantage it provides, as well as the seamless support for non-ASCII encodings and locales. – Intervalometerctype.h
, hence the use ofcctype
which necessitates thestatic_cast
. Anyway even though I don't want to include Boost, I recognize and share your readability concerns. The standard could do a lot better here. – Steenuint8_t
orunsigned char
before converting toint
because otherwise you may get unwanted sign extension depending on your platform! – Bailablestring
works withsigned char
s; why would I want to cast tounsigned char
when usingtolower
? – Steentolower
. So you have provided me with some helpful guidance. It's just not the answer that I want for this question. – Steenchar
which may or may not be signed. – Bailablestring
defined asbasic_string<char>
? So it will be signed? – Steenchar
may or may not be signed, that is implementation defined. – Raphaelchar
- type for character representation which can be most efficiently processed on the target system (has the same representation and alignment as either signed char or unsigned char, but is always a distinct type)." – Gaulsigned
modifier could you at least grace me with a citation from the standard? – Steenchar
type are represented as signed or unsigned quantities. Thesigned
specifier forceschar
objects to be signed; it is redundant in other contexts." 7.1.6.2 [decl.type.simple] in N4140. – Raphaelunsigned char
or be equal toEOF
. Thus calling them directly with plainchar
is invalid if it is signed and the value is negative. – Caponizesigned char[]
usingcctype
'stolower
is invalid o.O – Steenchar[]
. See this. – Raphaellocale
'stolower
works withchar
s notint
s, it should be preferred then? That may be as good an argument as any as far as why I should choose one over the other. Are you interested in writing it up, if not I can. – Steen