Standard math functions as operations in std::transform calls

  • Thread starter =?ISO-8859-1?Q?Ney_Andr=E9_de_Mello_Zunino?=
  • Start date
?

=?ISO-8859-1?Q?Ney_Andr=E9_de_Mello_Zunino?=

Hello.

The following program:

#include <list>
#include <iterator>
#include <algorithm>
#include <cmath>
#include <iostream>

int main()
{
using namespace std;

float elem[] = { 4, 7, 2, 8, 12 };
list<float> val(elem, elem + sizeof elem / sizeof elem[0]);

cout << "ORIGINAL\n\t";
copy(val.begin(), val.end(), ostream_iterator<float>(cout, "\n\t"));

transform(val.begin(), val.end(), val.begin(), log);

cout << "\nTRANSFORMED\n\t";
copy(val.begin(), val.end(), ostream_iterator<float>(cout, "\n\t"));

cout << '\n';
}

fails to compile in g++ 4.0, giving the following error message:

tran.cpp: In function `int main()':
tran.cpp:17: no matching function for call to `transform(
std::_List_iterator<float, float&, float*>, std::_List_iterator<float,
float&, float*>, std::_List_iterator<float, float&, float*>, <unknown
type>
)'

"Unknown type"?! I wondered why the compiler was not being able to
deduce it. Nothing occurred to me except the fact that the std::log
function (as most other math functions) has a handful of overloaded
versions. Could that be causing the compiler to get confused?

I then decided to try the same code in VC++ 8.00 on Windows. The program
compiles and produces the following expected results:

ORIGINAL
4
7
2
8
12

TRANSFORMED
1.38629
1.94591
0.693147
2.07944
2.48491

I would appreciate if anyone could let me know whether that program is
valid.

Thank you,
 
A

Andrey Tarasevich

Ney said:
...
fails to compile in g++ 4.0, giving the following error message:

tran.cpp: In function `int main()':
tran.cpp:17: no matching function for call to `transform(
std::_List_iterator<float, float&, float*>, std::_List_iterator<float,
float&, float*>, std::_List_iterator<float, float&, float*>, <unknown
type>
)'

"Unknown type"?! I wondered why the compiler was not being able to
deduce it. Nothing occurred to me except the fact that the std::log
function (as most other math functions) has a handful of overloaded
versions. Could that be causing the compiler to get confused?

Yes, exactly.

To make it work you have to choose one specific overloaded version of 'log'
manually:

transform(val.begin(), val.end(), val.begin(), (float (*)(float)) log);
I then decided to try the same code in VC++ 8.00 on Windows. The program
compiles and produces the following expected results:
...

The older version of VC++ are known to suppress the overloaded versions of match
functions (unless it is explicitly requested by [un]defining some MS-specific
macro). That could be the case with you compiler as well, which is what makes
the code to compile (just a guess).
 
A

amparikh

Ney said:
Hello.

The following program:

#include <list>
#include <iterator>
#include <algorithm>
#include <cmath>
#include <iostream>

int main()
{
using namespace std;

float elem[] = { 4, 7, 2, 8, 12 };
list<float> val(elem, elem + sizeof elem / sizeof elem[0]);

cout << "ORIGINAL\n\t";
copy(val.begin(), val.end(), ostream_iterator<float>(cout, "\n\t"));

transform(val.begin(), val.end(), val.begin(), log);

cout << "\nTRANSFORMED\n\t";
copy(val.begin(), val.end(), ostream_iterator<float>(cout, "\n\t"));

cout << '\n';
}

fails to compile in g++ 4.0, giving the following error message:

tran.cpp: In function `int main()':
tran.cpp:17: no matching function for call to `transform(
std::_List_iterator<float, float&, float*>, std::_List_iterator<float,
float&, float*>, std::_List_iterator<float, float&, float*>, <unknown
type>
)'

"Unknown type"?! I wondered why the compiler was not being able to
deduce it. Nothing occurred to me except the fact that the std::log
function (as most other math functions) has a handful of overloaded
versions. Could that be causing the compiler to get confused?

I then decided to try the same code in VC++ 8.00 on Windows. The program
compiles and produces the following expected results:

ORIGINAL
4
7
2
8
12

TRANSFORMED
1.38629
1.94591
0.693147
2.07944
2.48491

I would appreciate if anyone could let me know whether that program is
valid.

Thank you,

Isnt it because of the linkage issue ? Vandervoorde's book describes
about why you need either to typecast or provde an intermediate adapter.
 
?

=?ISO-8859-1?Q?Ney_Andr=E9_de_Mello_Zunino?=

Andrey said:
Ney André de Mello Zunino wrote:
[snip]
"Unknown type"?! I wondered why the compiler was not being able to
deduce it. Nothing occurred to me except the fact that the std::log
function (as most other math functions) has a handful of overloaded
versions. Could that be causing the compiler to get confused?

Yes, exactly.

To make it work you have to choose one specific overloaded version of 'log'
manually:

transform(val.begin(), val.end(), val.begin(), (float (*)(float)) log);

I had thought that the compiler might be able to figure out which
overloaded version of the function to call because it could tell the
type of the argument passed to it (inside std::transform) at compile
time. But that's probably not such an easy task.

The pointer-to-function cast doesn't look pretty, but I guess my code
had better include it if I want it to be portable.

[snip]

Regards,
 
A

Andrey Tarasevich

Ney said:
...

I had thought that the compiler might be able to figure out which
overloaded version of the function to call because it could tell the
type of the argument passed to it (inside std::transform) at compile
time. But that's probably not such an easy task.
...

Unfortunately, in order to obtain a meaningful body for 'std::transform' (i.e.
specialize it) the compiler has to know all template arguments. And functor type
is one of them, meaning that it has to be supplied in advance. Moreover,
apparently the template interface of 'std::transform' is not supposed to require
that functor's parameter type (and return type) is identical to the iterators'
value type. In some case, for example, you might want to apply, say, 'int
abs(int)' to a vector of 'float's in order to perform truncation and take the
absolute value at the same time.

However, there's still a generic technique that enables the "postponed" overload
resolution in cases like this, but it is based on using a functor of class type,
not a regular function pointer. If we define a class like this

struct Log {
template<typename T> T operator()(T v) { return log(v); }
};

and pass an object of this class to 'std::transform'

transform(val.begin(), val.end(), val.begin(), Log());

the appropriate version of 'log' will indeed be generated and called
automatically based on the actual argument type.
 

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

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top