Terminology in english :)

T

Taras_96

Hi everyone,

In me trying to find strtoupper wasn't working with transform (which
I've fixed now), I came across the following message:

| ------------------- cut here ---------------------
|
| #include <string>
| #include <algorithm>
| #include <cctype>
| #include <iostream
|
| using namespace std;
|
| std::string& lower( std::string& p_str )
| {
| std::transform( p_str.begin(), p_str.end(), p_str.begin(),
tolower );

^^^^^^^

This ought to be a FAQ.

Three facts:

1) std::tolower (resp. std::toupper) is an overloaded function.
2) C++ has no type for the set of overloaded functions.
3) Template-argument deduction works by unifying the argument type
with
function parameter types.

by point 2), point 3) cannot succeed, hence the error.

-- Gaby

=================

I don't really understand what points 2 & 3 are saying. Is point 3
saying that if we have a templated function:

<template X>foo(X x)

Then when we write:

foo('a')

The compiler deduces that in this function call X refers to a char
type?

No idea what point #2 is saying :)

On a related note:

" transform(foo.begin(), foo.end(), foo.begin(), (int(*)
(int))std::tolower);

The cast causes, on the one hand, an explicit overload resolution
in favour of the function; it also allows the compiler to properly
deduce the third argument to transform."

What does he mean we talking about casting causing an explicit
overload resolution in favour of the function?

Cheers

Taras
 
V

Victor Bazarov

Taras_96 said:
Hi everyone,

In me trying to find strtoupper wasn't working with transform (which
I've fixed now), I came across the following message:


^^^^^^^

This ought to be a FAQ.

Three facts:

1) std::tolower (resp. std::toupper) is an overloaded function.
2) C++ has no type for the set of overloaded functions.
3) Template-argument deduction works by unifying the argument type
with
function parameter types.

by point 2), point 3) cannot succeed, hence the error.

-- Gaby

=================

I don't really understand what points 2 & 3 are saying. Is point 3
saying that if we have a templated function:

<template X>foo(X x)

Return value type is missing.
Then when we write:

foo('a')

The compiler deduces that in this function call X refers to a char
type?

Yes. It's called "template argument deduction from a function
call".
No idea what point #2 is saying :)

If I were to speculate, it might be saying that no overloading
exists solely on the return value type. Or it might be saying
that there is no generic type in the language formed from a set
of overloaded functions (not sure what it might be useful for,
however).
On a related note:

" transform(foo.begin(), foo.end(), foo.begin(), (int(*)
(int))std::tolower);

The cast causes, on the one hand, an explicit overload resolution
in favour of the function; it also allows the compiler to properly
deduce the third argument to transform."

What does he mean we talking about casting causing an explicit
overload resolution in favour of the function?

Overload resolution when conversion to a function pointer is used
has to take the _destination_type_ into consideration. For
example:

int foo(int); // line 1
void foo(char);
int main() {
int (*p)(int) = foo; // the 'foo' from line 1 is picked
}

HTH

V
 
J

James Kanze

In me trying to find strtoupper wasn't working with transform
(which I've fixed now), I came across the following message:
| ------------------- cut here ---------------------
|
| #include <string>
| #include <algorithm>
| #include <cctype>
| #include <iostream
|
| using namespace std;
|
| std::string& lower( std::string& p_str )
| {
| std::transform( p_str.begin(), p_str.end(), p_str.begin(),
tolower );

This ought to be a FAQ.
Three facts:
1) std::tolower (resp. std::toupper) is an overloaded function.
2) C++ has no type for the set of overloaded functions.
3) Template-argument deduction works by unifying the argument type
with
function parameter types.
by point 2), point 3) cannot succeed, hence the error.

I don't really understand what points 2 & 3 are saying. Is
point 3 saying that if we have a templated function:
<template X>foo(X x)
Then when we write:

The compiler deduces that in this function call X refers to a
char type?
No idea what point #2 is saying :)

That std::toupper is not one function, but many (an infinite
number of functions, in fact), and that because of this, it
doesn't have a type. In order to have a type, you have to
resolve the overloading. In this particular case (but it's
about the only case where this is true in C++), the compiler
will use the target type to resolve overloading. Except that
since this parameter of std::transform is a template parameter,
all possible, the target type will be determined by argument
deducction.

An endless loop, in fact: in order to do template argument
deduction, we have to know the type of the argument, and in
order to determine the type of the argument, we have to know the
arguments of the template.
On a related note:
" transform(foo.begin(), foo.end(), foo.begin(), (int(*)
(int))std::tolower);
The cast causes, on the one hand, an explicit overload resolution
in favour of the function; it also allows the compiler to properly
deduce the third argument to transform."
What does he mean we talking about casting causing an explicit
overload resolution in favour of the function?

It means that in cases where the compiler cannot resolve
overloading when taking the address of an overloaded function,
the language has provided a hack so we can manually specify
which function we want. Syntactically, it's a cast, but
semantically, it doesn't do a conversion, but specifies what we
expect the type to actually be.

(Note that this is a bit tricky. If I replace std::tolower with
a function that isn't overloaded, and which has a different
signature than the one given, it *is* a conversion.)

While I'm at it: your code won't work anyway: you can't call the
one argument version of std::tolower with a dereferences string
iterator without incurring undefined behavior. You need to use
the two argument version (with a locale) in <locale>. (More
generally, you need to define what you really mean by "tolower".
There's not a one to one mapping, or even a many to one mapping,
of upper case to lower case: sometimes, the lower case version
of a string will result in a different number of characters, and
sometimes, the conversion will depend on the context.)
 
A

Andrey Tarasevich

Taras_96 said:
...
| std::string& lower( std::string& p_str )
| {
| std::transform( p_str.begin(), p_str.end(), p_str.begin(),
tolower );

^^^^^^^

This ought to be a FAQ.

Three facts:

1) std::tolower (resp. std::toupper) is an overloaded function.
2) C++ has no type for the set of overloaded functions.
3) Template-argument deduction works by unifying the argument type
with
function parameter types.

by point 2), point 3) cannot succeed, hence the error.

-- Gaby

=================

I don't really understand what points 2 & 3 are saying.

Let me try to explain what he's trying to say in 2.

Function template 'std::transform' is declared as follows

template<class InIt, class OutIt, class Unop>
OutIt transform(InIt first, InIt last, OutIt x, Unop uop);

If you take a look inside the definition of 'std::transform' template,
you'll see that at some point it calls the function (or, more precisely,
the functor) passed to 'transform' through its last parameter 'uop' of
type 'Unop'. In your case it is 'std::tolower'. Note, that at the point
of the call the number and the types of the 'tolower's arguments are
known. So, if somehow we could ask compiler to delay overload resolution
for the 'tolower' to the point where it is actually _called_ (inside
'transform'), the compiler would be able to figure out which 'tolower'
function to use by itself, automatically, without any help from the user.

In other words, if somehow we could pass the entire family of overloaded
'tolower' functions as an argument for 'transform' template and expect
the compiler to choose the concrete version to call later (at the point
of the 'tolower' call), then we wouldn't have to worry about selecting
the concrete version of 'tolower' manually. Unfortunately, as long as we
stick with an ordinary 'tolower' function, it is not possible to do in
C++. The compiler has to know the concrete type for the template
parameter 'Unop' and a concrete value for the 'uop' parameter, it wants
to know them _early_, and we have to supply them by manually forcing the
early overload resolution. (This is what the cast does).

At the same time I'd like to note there's a well-known template
programming technique which actually achieves exactly what is mentioned
by that poster. By using functional objects we can _decouple_ the
process of template argument deduction from the process of overload
resolution. In a sense, this technique actually does "pass" the entire
family of overloaded functions to a template and then makes the compiler
to select the right member of the family later, at the point of the
actual call. However, once again, it requires use of function objects,
instead of regular function pointers. Take a look at the following
example, which creates a functional object wrapper for the entire family
of overloaded 'tolower' functions

struct tolower_wrapper {

// This is the "old" C-style version
int operator ()(int c) const
{ return std::tolower(c); }

// And this is the new C++ version
template<typename C> C operator()(C c, const std::locale& loc) const
{ return std::tolower(c, loc); }

};

This wrapper "wraps" both the "old" C-style version and the new
C++-style version of 'tolower' function, providing the access through
its overloaded 'operator()' methods. Now we can call 'std::transform' as
follows

std::transform( p_str.begin(), p_str.end(), p_str.begin(),
tolower_wrapper() );

In this case the type of the template argument is immediately
automatically deduced as 'tolower_wrapper' and it doesn't involve any
overload resolution at all. The overload resolution will come into play
later, when 'transform' will try to apply the function call operator
'(<argument>)' to its last parameter. The compiler will be able to
perform resolution automatically, without any help from the user.

However, as you can see, it requires the use of a functional object. It
can't be done with ordinary function pointers. This is exactly what 2 is
saying.
On a related note:

" transform(foo.begin(), foo.end(), foo.begin(), (int(*)
(int))std::tolower);

The cast causes, on the one hand, an explicit overload resolution
in favour of the function; it also allows the compiler to properly
deduce the third argument to transform."

What does he mean we talking about casting causing an explicit
overload resolution in favour of the function?

Well, that exactly what it does. When you cast an overloaded function to
a certain function pointer type, the compiler will perform overload
resolution and select the function that matches the target type (the one
used in the cast).

Once again, 'std::transform' is defined as follows

template<class InIt, class OutIt, class Unop>
OutIt transform(InIt first, InIt last, OutIt x, Unop uop);

The compiler has to know the types of all three template parameters -
'InIt', 'OutIt' and 'Unop'. In your original expression

std::transform( p_str.begin(), p_str.end(), p_str.begin(), tolower )

the first two can be easily deduced automatically. The third one can't
be because the compiler doesn't know which version of 'tolower' you want
to use. By using the cast you tell the compiler which version to use.

BTW, there's another way to tell the compiler what you want: use
explicitly specified template arguments

std::transform<
std::string::iterator,
std::string::iterator,
int(*)(int)>( p_str.begin(), p_str.end(), p_str.begin(), tolower )

This should also work, but it is even less elegant than the original
version. (Although for some reason it doesn't compiler with Comeau. Am I
missing something?)
 
G

Gerhard Fiedler

It means that in cases where the compiler cannot resolve overloading
when taking the address of an overloaded function, the language has
provided a hack so we can manually specify which function we want.
Syntactically, it's a cast, but semantically, it doesn't do a
conversion, but specifies what we expect the type to actually be.

(Note that this is a bit tricky. If I replace std::tolower with a
function that isn't overloaded, and which has a different signature than
the one given, it *is* a conversion.)

Is there a way to explicitly specify which of the overloads to use that
fails if there isn't an exact match?

Gerhard
 
A

Andrey Tarasevich

Gerhard said:
...
Is there a way to explicitly specify which of the overloads to use that
fails if there isn't an exact match?
...

Use explicit specification of template arguments instead of a cast - it will
fail if there's no exact match

std::transform<
std::string::iterator,
std::string::iterator,
int(*)(int)>( p_str.begin(), p_str.end(), p_str.begin(), std::tolower )

Unfortunately, in this case the ambiguous parameter is the last one of three,
which means that we have to specify all of them explicitly.
 
J

James Kanze

On 2008-02-28 14:07:40, James Kanze wrote:
Is there a way to explicitly specify which of the overloads to
use that fails if there isn't an exact match?

Two casts, the first to specify the overload you want, and the
second to do the conversion. You don't want this here,
however; the only thing you can legally do with the converted
value is cast it back to the original type, and transform isn't
about to do that.
 
A

Andrey Tarasevich

Gerhard said:
...
Is there a way to explicitly specify which of the overloads to use that
fails if there isn't an exact match?
...

Actually, just using a 'static_cast' instead of the C-style cast should already
achieve that. The original version does not fail if there isn't an exact match
specifically because it uses a brutish C-style cast.
 
G

Gerhard Fiedler

Actually, just using a 'static_cast' instead of the C-style cast should already
achieve that. The original version does not fail if there isn't an exact match
specifically because it uses a brutish C-style cast.

Is a "brutish C-style cast" <g> the same as a reinterpret_cast?

Thanks,
Gerhard
 
B

Bo Persson

Gerhard said:
Is a "brutish C-style cast" <g> the same as a reinterpret_cast?

No, it is rather a "whatever it takes"_cast. The compiler will have to
do any combinations of C++ casts, or even worse things, to achive the
just-do-it effect.

Makes it hard to see what happens. :)


Bo Persson
 
J

Jeff Schwab

Gerhard said:
Is a "brutish C-style cast" <g> the same as a reinterpret_cast?

Aside from being difficult to grep for, and doing less than
reinterpret_cast (et al) to make the programmer's intent clear, C-style
casts may ("oops") perform unintended conversions. For example, a
reinterpret_cast won't cast away const, but a C-style cast might.

int main() {
int const i = 42;

// Won't compile.
// int j = reinterpret_cast<int>(i);

// Equivalent to const_cast.
int k = (int) i;
}
 
G

Gerhard Fiedler

No, it is rather a "whatever it takes"_cast. The compiler will have to
do any combinations of C++ casts, or even worse things, to achive the
just-do-it effect.

Is there anything that couldn't be achieved with a combination of C++
casts? It seems that the only thing a reinterpret_cast doesn't do is cast
away const (as Jeff explained), which can be done by a const_cast.

Gerhard
 
J

James Kanze

On 2008-03-01 13:12:27, Bo Persson wrote:
Is there anything that couldn't be achieved with a combination
of C++ casts?

Yes. C style casts ignore access, the new style casts don't.
So you can do things like:

class Base {} ;
class Derived : private Base {} ;

Base* p = (Base*)new Derived ;

There's no new style cast which will allow this.
 
T

Taras_96

While I'm at it: your code won't work anyway: you can't call the
one argument version of std::tolower with a dereferences string
iterator without incurring undefined behavior. You need to use
the two argument version (with a locale) in <locale>. (More
generally, you need to define what you really mean by "tolower".
There's not a one to one mapping, or even a many to one mapping,
of upper case to lower case: sometimes, the lower case version
of a string will result in a different number of characters, and
sometimes, the conversion will depend on the context.)

Could you explain this a bit further? You've kind of lost me :). Why
is the behaviour undefined?
 
T

Taras_96

Thanks Andrey, James, and Victor for your excellent responses.

So, if I understand correctly:

1) Overload resolution can be done by the compiler where the function
is called
2) Since transform is templated, it is overloaded (and it also has a C
version)
3) In this case, the overload resolution must be done where transform
is called (template argument deduction from a functioncall). But in
order for template argument deduction to be done, we must tell the
compiler what 'version' of the function to use. This is what the cast
does.
4) By using functors we can accomplish delaying the overload
resolution until the function is actually called within the transform
function, similar to 1).

Thanks again

Taras
 
J

James Kanze

Could you explain this a bit further? You've kind of lost me
:). Why is the behaviour undefined?

Because the one argument form of tolower, declared in <cctype>,
takes an int in the range [0...UCHAR_MAX] (or EOF) as an
argument---anything else is undefined behavior (dixit the C
standard). If char is signed (and it often is), you can get
something else. If you want to use any of the functions
declared in <cctype> with a char, you should always cast it to
unsigned char before passing it in as an argument. (More
generally, of course, you want to avoid those functions anyway,
since they depend on global state, whose value may change
without your being aware of it.)
 
J

James Kanze

Thanks Andrey, James, and Victor for your excellent responses.
So, if I understand correctly:
1) Overload resolution can be done by the compiler where the function
is called
2) Since transform is templated, it is overloaded (and it also has a C
version)

Not quite (and it certainly doesn't have a C version). Since
transform is a function template, it doesn't exist as a function
until the compiler has worked out what arguments to use. In
theory, you could specify them explicitly, e.g.
std::transform< ... >( ... ) ;
In practice, you're not going to want to. (The first template
argument is something like std::basic_string< char,
std::char_traits< char >, std::allocator >::iterator. And the
others aren't any simpler.) You'll want compiler to do
automatic type deduction. But the compiler can't do that until
it knows that type it has to deduce from.
3) In this case, the overload resolution must be done where transform
is called (template argument deduction from a functioncall). But in
order for template argument deduction to be done, we must tell the
compiler what 'version' of the function to use. This is what the cast
does.
Exactly.

4) By using functors we can accomplish delaying the overload
resolution until the function is actually called within the transform
function, similar to 1).

That's not really the difference. In this case, you're
arranging for the type of the unary_function to be a
non-template class type. No template, and the compiler knows
the exact type.
 
T

Taras_96

Not quite (and it certainly doesn't have a C version). Since
transform is a function template, it doesn't exist as a function
until the compiler has worked out what arguments to use. In
theory, you could specify them explicitly, e.g.
std::transform< ... >( ... ) ;
In practice, you're not going to want to. (The first template
argument is something like std::basic_string< char,
std::char_traits< char >, std::allocator >::iterator. And the
others aren't any simpler.) You'll want compiler to do
automatic type deduction. But the compiler can't do that until
it knows that type it has to deduce from.

My statement may have made more sense if I used the correct function
name - toupper! Somehow I made this mistake.

That's not really the difference. In this case, you're
arranging for the type of the unary_function to be a
non-template class type. No template, and the compiler knows
the exact type.

And by arranging it to be a non-template class type, we delay overload
resolution until the function is called?
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,078
Latest member
MakersCBDBlood

Latest Threads

Top