void (*) () == [](){} ? trouble with templates, lambda and pointer to function

Y

ybailly

Greetings all,

Here's a problem I have, here reduced to the minimum I could get.
Please consider the following complete program:

1 #include <iostream>
2
3 struct S
4 {
5 typedef void (*slot_t) ();
6 };
7
8 template<typename T>
9 struct ST
10 {
11 typedef T slot_t;
12 };
13
14 void m(S::slot_t f)
15 {
16 std::cout << "::m(S::slot_t)\n";
17 f();
18 }
19
20 template<typename T>
21 void m(typename ST<T>::slot_t f)
22 {
23 std::cout << "::m<T>(ST<T>::slot_t)\n";
24 f();
25 }
26
27 void ff()
28 {
29 std::cout << "::ff()\n";
30 }
31
32 int main(int /*argc*/, char* /*argv*/[])
33 {
34 auto ll = [](){ std::cout << "main()::<lambda>()\n"; };
35
36 m(ff);
37 m(ll);
38 m<decltype(ll)>(ll);
39
40 return 0;
41 }

Using GCC 4.6.3 and GCC 4.7.0, this program is compiled
using this command-line:
$ g++-4.7 -std=c++0x -Wall -Wextra -pedantic -o test.x test.cpp

Gives no error, no warning. When executed, I get:
$ ./text.x
::m(S::slot_t)
::ff()
::m(S::slot_t)
main()::<lambda>()
::m<T>(ST<T>::slot_t)
main()::<lambda>()

I assumed the call line 37 would be resolved in calling the template declared lines 20-25, but it just calls the global, non-template line 14. The call line 38 is the wanted behavior, though I would prefer to avoid giving the template parameter.

This makes me believe that the type of the lambda line 34 is the same as the type of the function ff() line 27 - or at least it can be converted to.
Is this correct?

How can I have the behavior like line 38, without giving explicitly the template parameter?

Thanks for any hint and have all a nice day :)
 
B

Bo Persson

ybailly skrev 2012-06-23 09:46:
Greetings all,

Here's a problem I have, here reduced to the minimum I could get.
Please consider the following complete program:

1 #include <iostream>
2
3 struct S
4 {
5 typedef void (*slot_t) ();
6 };
7
8 template<typename T>
9 struct ST
10 {
11 typedef T slot_t;
12 };
13
14 void m(S::slot_t f)
15 {
16 std::cout << "::m(S::slot_t)\n";
17 f();
18 }
19
20 template<typename T>
21 void m(typename ST<T>::slot_t f)
22 {
23 std::cout << "::m<T>(ST<T>::slot_t)\n";
24 f();
25 }
26
27 void ff()
28 {
29 std::cout << "::ff()\n";
30 }
31
32 int main(int /*argc*/, char* /*argv*/[])
33 {
34 auto ll = [](){ std::cout << "main()::<lambda>()\n"; };
35
36 m(ff);
37 m(ll);
38 m<decltype(ll)>(ll);
39
40 return 0;
41 }

Using GCC 4.6.3 and GCC 4.7.0, this program is compiled
using this command-line:
$ g++-4.7 -std=c++0x -Wall -Wextra -pedantic -o test.x test.cpp

Gives no error, no warning. When executed, I get:
$ ./text.x
::m(S::slot_t)
::ff()
::m(S::slot_t)
main()::<lambda>()
::m<T>(ST<T>::slot_t)
main()::<lambda>()

I assumed the call line 37 would be resolved in calling the template declared lines 20-25, but it just calls the global, non-template line 14. The call line 38 is the wanted behavior, though I would prefer to avoid giving the template parameter.

This makes me believe that the type of the lambda line 34 is the same as the type of the function ff() line 27 - or at least it can be converted to.
Is this correct?

How can I have the behavior like line 38, without giving explicitly the template parameter?

This has less to do with the type of the lambda, and more with type
deduction for the function template. The function

20 template<typename T>
21 void m(typename ST<T>::slot_t f)

is a "non-deducible context", meaning that the compiler cannot deduce T
from this without instantiating ALL possible S<T> and check for a member
typedef. So it doesn't, and calls the non-template function instead.

Only when you explicitly tell it what T is (on line 38) does it call the
function template.


Bo Persson
 
Y

ybailly

Hello Bo,

ybailly skrev 2012-06-23 09:46:
[...]

This has less to do with the type of the lambda, and more with type
deduction for the function template. The function

20 template<typename T>
21 void m(typename ST<T>::slot_t f)

is a "non-deducible context", meaning that the compiler cannot deduce T
from this without instantiating ALL possible S<T> and check for a member
typedef. So it doesn't, and calls the non-template function instead.

Thanks for your answer, I think I get it.

Now the question is, given a template like this:
template<typename T>
void f(T val) { /* ... */ }

....how can I "guess" if T is actually a lambda? Can something like the following work:
if ( std::is_pointer<T>::value )
{
typedef std::remove_pointer<T>::type Trp;
if ( std::is_class<Trp>::value )
{
// it's a class, a functor
}
else if ( std::is_function<Trp>::value )
{
// it's a function
}
else if ( (not std::is_object<Trp>::value) and
(not std::is_fundamental<Trp>::value) )
{
// it's a lambda... or not?
}
}
 
L

Luca Risolia

3 struct S
4 {
5 typedef void (*slot_t) ();
6 };
7
14 void m(S::slot_t f)
15 {
16 std::cout<< "::m(S::slot_t)\n";
17 f();
18 }
34 auto ll = [](){ std::cout<< "main()::<lambda>()\n"; };
37 m(ll);
This makes me believe that the type of the lambda line 34 is the same as the type of the function ff() line 27 - or at least it can be converted to.
Is this correct?

The standard makes that more clear:

"The type of the lambda-expression (which is also the type of the
closure object) is a unique, unnamed nonunion class type — called the
closure type —"

and

"The closure type for a lambda-expression with no lambda-capture has a
public non-virtual non-explicit const conversion function to pointer to
function having the same parameter and return types as the closure
type’s function call operator."
 
V

Victor Bazarov

[..]
Now the question is, given a template like this:
template<typename T>
void f(T val) { /* ... */ }

...how can I "guess" if T is actually a lambda? [..]

Trying to guess what the template argument is to perform different
actions for different types is just A BAD IDEA(tm). That's what
template specializations are for (if you really need to step away from
generic programming). Otherwise, you shouldn't be using templates in
the first place if your code is *specific* instead of being *generic*.

V
 
L

Luca Risolia

Now the question is, given a template like this:
template<typename T>
void f(T val) { /* ... */ }

...how can I "guess" if T is actually a lambda?

What you can do is to specialize f for a given lambda-expression. In
your case:

auto ll = [](){ std::cout << "main()::<lambda>()\n"; };

template<>
void f<decltype(ll)>(decltype(ll) val) { /*...*/ }

ll must be defined before the template specialization.
 
S

SG

Trying to guess what the template argument is to perform different
actions for different types is just A BAD IDEA(tm).

Totally agreed.
That's what
template specializations are for (if you really need to step away from
generic programming).

Prefer overloading where possible.

Cheers!
SG
 
Y

ybailly

[..]
Now the question is, given a template like this:
template<typename T>
void f(T val) { /* ... */ }

...how can I "guess" if T is actually a lambda? [..]

Trying to guess what the template argument is to perform different
actions for different types is just A BAD IDEA(tm). That's what
template specializations are for (if you really need to step away from
generic programming). Otherwise, you shouldn't be using templates in
the first place if your code is *specific* instead of being *generic*.

I may agree with that... the problem being I can't specialize my
template for lambda, as I may not know in advance what the kind
of lambda I'm given...

template<typename T>
void f(T t) { /* generic case */ }

template<>
void f<WHAT?>(lambda l) { /* lambda case */ }

moreover:
int i;
auto l1 = [&i](double d) { i = d; };
auto l2 = [&i](double d) { i = d; };

If I understood everything correctly, l1 and l2 don't have
the same type, have they?
 
V

Victor Bazarov

[..]
Now the question is, given a template like this:
template<typename T>
void f(T val) { /* ... */ }

...how can I "guess" if T is actually a lambda? [..]

Trying to guess what the template argument is to perform different
actions for different types is just A BAD IDEA(tm). That's what
template specializations are for (if you really need to step away from
generic programming). Otherwise, you shouldn't be using templates in
the first place if your code is *specific* instead of being *generic*.

I may agree with that... the problem being I can't specialize my
template for lambda, as I may not know in advance what the kind
of lambda I'm given...

template<typename T>
void f(T t) { /* generic case */ }

template<>
void f<WHAT?>(lambda l) { /* lambda case */ }

moreover:
int i;
auto l1 = [&i](double d) { i = d; };
auto l2 = [&i](double d) { i = d; };

If I understood everything correctly, l1 and l2 don't have
the same type, have they?

They don't, that's correct.

My question to you: why do you think you need to make the distinction
between a lambda and everything else? I tried understanding the code
you posted in your original message in this thread, but those numbers at
the beginning of every line just get in the way of reading the code.
Perhaps you could elaborate on the problem you're trying to solve...

V
 
S

Stuart Redmann

On 25 Jun., Victor Bazarov wrote:

[snip]


Welcome back. You have already been missed (see posting from Hoshua on
May, 25th). Hope it was holidays that kept you away from this group.

Stuart
 

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,968
Messages
2,570,154
Members
46,702
Latest member
LukasConde

Latest Threads

Top