Assigning lambdas

Started by
26 comments, last by Krohm 11 years, 11 months ago
...

You appear to still not be understanding the issue.

Furthermore, it works in GCC... RIGHT UP UNTIL YOU DO SOMETHING WITH THE STACK. At which point it breaks horribly. FOR THE EXACT SAME REASON.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.

Advertisement
As others have pointed out, lambas are not an instance of std::function<>. Microsoft in this instance have done nothing wrong.


Works fine in GCC. Perhaps a compiler bug?

I mean seriously... we are bashing even the expected thing just because we want to be formally correct?
Because in my opinion what GCC is doing is The Right Thing and I don't think they have made it work because of a misconception, an error or a bug. I think it's the right thing to do if we claim to have support for "functional programming" in some language - define a function as you want - you don't change what it is.
[/quote]
Note: I just ran the code, I didn't notice storing the value by const reference. As such, my comment was incorrect. I should have said, at most, "Appears to work fine in GCC".
My problem here is that I never figured out that the compiler would have cast something in the first place and yes, it was clear to me there were different types going to clash.[/quote]

It's not a cast, it's implicit conversion.

Going back to string example:void foo(const string & str);

const char * s;
foo(s);
Obviously, s cannot be passed to str, they are unrelated types.

But string has a constructor which takes const char *.

So compiler creates new string by passing s into its constructor. This is the temporary object. Same thing happens with lambdas - a new function<> instance is created as temporary.

Which is why it's recommended to declare constructors with 'explicit' to prevent these accidental conversions. String doesn't use explicit to work transparently with C-style strings. Otherwise, one would need to manually perform the conversion:foo(std::string(s)); // which is potentially ambiguous as function call
// so
std::string temp(s);
foo(temp);
// vs
foo("Hello World");




As a side note I still don't quite understand why references cannot be copied.[/quote]

They can be, but don't produce desirable result. Simple example, using pointers to make it clearer:struct A {
int * x;
}

A a;
a.x = new int(10);

A b;
b.x = a.x; // wrong
delete a.x; // boom
---
b.x = new int( *a.x); // right, deep copy
delete a.x; // no problem

It is impossible to properly implement assignment operator or rule of three with references alone - and there is no mechanism to provide a deep copy, since references don't provide life cycle information. Even if they did, it wouldn't help, since they might refer to read-only storage, stack, part of another structure, heap or just about any other valid memory location, many of which cannot be copied, but can be destroyed.
No. I don't understand. And clearly, I cannot even explain. To quote myself:

This relies on a subtle detail: the fact that push_back performs a value copy.
And that was clear hours ago. Now I get an explanation for the delete semantics of shared pointers... ok, I get the point that I am the only one thinking a function should be the same "thing" whatever it was declared/defined - because that would imply no casts/conversions would be necessary.
The choice of the word "cast" was not taken lightly: in my understanding of the system a pointer cast (supposing doing derived-to-base pointer conversions qualify as a cast) would have been sufficient but ok, there's no problem. My understanding of the feature was incorrect that's now clear because I had the impression the system was completely "fluid". The thing that was not clear to me as I written already was that different types were involved - as I noted, I would have expected them to alias. Because of "language support" but I clearly overstated the capability.

How are lambda types supposed to be expressed in declarations? I would be tempted to do something like

typedef [](){}->void PrintFunc;

But it does not appear to work. I think I've seen somewhere something involved instantiating a function and capturing its type. Is [font=courier new,courier,monospace]std::function[/font] still the way to do this? Do you have any hints to protect myself from eventual typos - I am used to work mostly by referencing and I guess I could benefit sooner or later from a guard.

Previously "Krohm"

The type of a lambda function is a unique anonymous functor type for every lambda function. Two separate lambdas that have the exact same definition in the exact same scope will have two separate types. As such, trying to get at the exact type of a lambda function is an exercise in futility. Just store the std::function by value.

The type of a lambda function is a unique anonymous functor type for every lambda function. Two separate lambdas that have the exact same definition in the exact same scope will have two separate types. As such, trying to get at the exact type of a lambda function is an exercise in futility. Just store the std::function by value.

With that in mind, it made me curious if
auto lines = []() { cout<<"- - - - - -"<<endl; };
auto dots = []() { cout<<". . . . . ."<<endl; };
lines = dots;

works. I don't have a compiler on my current machine to test if the assignment gives a compile time error. [s]if no one answers this before I get to a proper computer and can test this myself, I'll just edit this with what I found.[/s]

[edit]

Got that sooner than I thought I would. For my little test, the type of [font=courier new,courier,monospace]lines[/font] is [font=courier new,courier,monospace]const main()::<lambda()>&[/font] and the assignment generates a compile time error.

However, a normal function pointer worked:
#include <iostream>
#include <functional>

typedef void (*function) ();

int main()
{
function lines = []() { std::cout << " - - - - - " << std::endl; };
auto dots = []() { std::cout << " . . . . . " << std::endl; };

lines = dots;

lines();
dots();
}


But yes, I would just go with a [font=courier new,courier,monospace]std::function[/font].
[size=2][ I was ninja'd 71 times before I stopped counting a long time ago ] [ f.k.a. MikeTacular ] [ My Blog ] [ SWFer: Gaplessly looped MP3s in your Flash games ]

However, a normal function pointer worked:

A conversion to function pointer exists for the function object types of lambdas that don't capture any variables. It should be pretty obvious why lambdas that do capture variables don't have that conversion.

[quote name='Cornstalks' timestamp='1336053544' post='4937102']
However, a normal function pointer worked:

A conversion to function pointer exists for the function object types of lambdas that don't capture any variables. It should be pretty obvious why lambdas that do capture variables don't have that conversion.
[/quote]


#include <cstdio>
#include <functional>

int main ()
{
volatile int a = 5;
std::function<void ()> func = [a] () { printf("%i\n", a); };
func();
return 0;
}


That code compiles and executes properly on both VC10, 11, and GCC.
Function pointer != function object.

This topic is closed to new replies.

Advertisement