Discussion:
Why do closures not have a default constructor?
(too old to reply)
Klaim - Joël Lamotte
2016-05-24 14:25:38 UTC
Permalink
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).

I cannot find a specific reason for that so far.
Does anyone here have a source about this point?


Otherwise I would like to propose to add

ClosureType() = default;

to the definition of the closure generated by a lambda, if it's not already
proposed somewhere.

This would help working with closures in generic context where we assume
that the callable type is regular (it's not enough but it helps);

For example with this kind of code:


template< class T, class Callable >
struct FatForwardIterator
{
T value;
Callable incr; // deactivate the default constructor if it's a closure
//... etc

FatForwardIterator& operator++()
{
// ...
value = incr(value);
return *this;
}
};
// let's assume that the proposal to generate default comparisons is on...


template< class T, class Callable >
auto make_fat_iterator( T initial_value, Callable incr )
{
return FatForwardIterator<T, Callable>{ initial_value, incr };
}

template< class IteratorType>
void some_algorithm( IteratorType begin, IteratorType end )
{
IteratorType previous; // error: IteratorType do not have a default
constructor.
do
{
previous = begin;
++begin;
// ... use previous
}
while( begin != end );
}


void foo()
{
auto times2 = [](auto value){ return value * 2; };
auto begin = make_fat_iterator( 1, times2 );
auto end = make_fat_iterator( 8, times2 );

// ...
some_algorithm( begin, end ); // fails to compile
}
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91ONZtVMdUZQ_JpyVwcV0%2BsXVdUUcSnmgrzr4u6viuVk5WQ%40mail.gmail.com.
Ville Voutilainen
2016-05-24 14:29:40 UTC
Permalink
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
If the lambda has captures, it's either insane or impossible to
default-construct it.
Treating non-capturing lambdas differently would require motivation that hasn't
appeared thus far, at least not in proposal form.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUYnh2_0dePfnbU%2Bk%3D3%2Bew1yid2t0%3Dv_Yb9VCjjhCUVzCQ%40mail.gmail.com.
Klaim - Joël Lamotte
2016-05-24 14:49:37 UTC
Permalink
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
If the lambda has captures, it's either insane or impossible to
default-construct it.
Why? I do not see any justification or explanation of what is "insane" so
far.
I do not see why a =default; could not be used
to rely on capture's type default constructors, deleting the default
constructor if it would not be possible for a normal type either.

If we had used a user-defined callable type instead of a lambda in the
example I gave, it would have worked as expected.
Is there a reason for this lack of homogeneity?
Treating non-capturing lambdas differently would require motivation that
Post by Ville Voutilainen
hasn't
appeared thus far, at least not in proposal form.
I agree that both cases should have the exact same behaviour, but this is
not really my point.
Post by Ville Voutilainen
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUYnh2_0dePfnbU%2Bk%3D3%2Bew1yid2t0%3Dv_Yb9VCjjhCUVzCQ%40mail.gmail.com
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91ONDgJNp5pc1513%3Ddk-infkAMqbQFbqXoT%2BNFtBQe6M2RA%40mail.gmail.com.
Viacheslav Usov
2016-05-24 14:56:04 UTC
Permalink
Post by Klaim - Joël Lamotte
Why? I do not see any justification or explanation of what is "insane" so
far.
Post by Klaim - Joël Lamotte
I do not see why a =default; could not be used
Do I understand correctly that you would expect that to fail when captures
were not default-constructible?

And what about captures by references? *What *would they be initialized
with?

Cheers,
V.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg0qVNfpi12bi_xKBw64q96xOoGw%3DR-SaiOKp8VT9ewFbQ%40mail.gmail.com.
Klaim - Joël Lamotte
2016-05-24 15:04:08 UTC
Permalink
Post by Klaim - Joël Lamotte
Post by Klaim - Joël Lamotte
Why? I do not see any justification or explanation of what is "insane"
so far.
Post by Klaim - Joël Lamotte
I do not see why a =default; could not be used
Do I understand correctly that you would expect that to fail when captures
were not default-constructible?
Yes.
Post by Klaim - Joël Lamotte
And what about captures by references? *What *would they be initialized
with?
The current behaviour (though I didn't check the exact wording) makes the
closure copyable even when capturing
by reference.
It is unspecified (if my memory is correct) if data have to be stored when
capturing by reference,
but when an implementation needs to store the implementation of the
reference capture,
it imply the the actual stored data behave like (or is actually) a pointer.
Pointers can be default constructed.


Cheers,
Post by Klaim - Joël Lamotte
V.
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg0qVNfpi12bi_xKBw64q96xOoGw%3DR-SaiOKp8VT9ewFbQ%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg0qVNfpi12bi_xKBw64q96xOoGw%3DR-SaiOKp8VT9ewFbQ%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91OMS-3RYKJ30jWUBAxXxsjqTBcrtL1FAO-humbxPrTa09Q%40mail.gmail.com.
Viacheslav Usov
2016-05-24 15:15:22 UTC
Permalink
On Tue, May 24, 2016 at 5:04 PM, Klaim - Joël Lamotte <***@gmail.com>
wrote:

And what about captures by references? *What *would they be initialized
Post by Klaim - Joël Lamotte
with?
The current behaviour (though I didn't check the exact wording) makes the
closure copyable even when capturing
by reference.
It is unspecified (if my memory is correct) if data have to be stored when
capturing by reference,
but when an implementation needs to store the implementation of the
reference capture,
it imply the the actual stored data behave like (or is actually) a pointer.
Pointers can be default constructed.
Is that a way of saying that captures by reference would be bound to
non-existing objects? Here is an excerpt from [dcl.ref] that makes this
problematic:

A reference shall be initialized to refer to a valid object or function. [
Note: in particular, a null reference cannot exist in a well-defined
program, because the only way to create such a reference would be to bind
it to the “object” obtained by indirection through a null pointer, which
causes undefined behavior. As described in 9.6, a reference cannot be bound
directly to a bit-field. — end note ]
[end]

Cheers,
V.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg3oH23ZGhhfcooqtj4jD%2Bfk0fG320db0YuO3eVi%3DOGC0Q%40mail.gmail.com.
Klaim - Joël Lamotte
2016-05-24 15:19:12 UTC
Permalink
Post by Viacheslav Usov
And what about captures by references? *What *would they be initialized
Post by Klaim - Joël Lamotte
with?
The current behaviour (though I didn't check the exact wording) makes the
closure copyable even when capturing
by reference.
It is unspecified (if my memory is correct) if data have to be stored
when capturing by reference,
but when an implementation needs to store the implementation of the
reference capture,
it imply the the actual stored data behave like (or is actually) a pointer.
Pointers can be default constructed.
Is that a way of saying that captures by reference would be bound to
non-existing objects? Here is an excerpt from [dcl.ref] that makes this
A reference shall be initialized to refer to a valid object or function. [
Note: in particular, a null reference cannot exist in a well-defined
program, because the only way to create such a reference would be to bind
it to the “object” obtained by indirection through a null pointer, which
causes undefined behavior. As described in 9.6, a reference cannot be bound
directly to a bit-field. — end note ]
[end]
Interesting. So even if I propose a zero-intiialization for reference
capture types, it would conflict with this.
Post by Viacheslav Usov
Cheers,
V.
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg3oH23ZGhhfcooqtj4jD%2Bfk0fG320db0YuO3eVi%3DOGC0Q%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg3oH23ZGhhfcooqtj4jD%2Bfk0fG320db0YuO3eVi%3DOGC0Q%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91OO1io2-XObK3hDSGVoDGoZ2Qan0rFJwoFVoqvsPMCgcJw%40mail.gmail.com.
Klaim - Joël Lamotte
2016-05-24 15:21:11 UTC
Permalink
Post by Klaim - Joël Lamotte
Post by Viacheslav Usov
And what about captures by references? *What *would they be initialized
Post by Klaim - Joël Lamotte
with?
The current behaviour (though I didn't check the exact wording) makes
the closure copyable even when capturing
by reference.
It is unspecified (if my memory is correct) if data have to be stored
when capturing by reference,
but when an implementation needs to store the implementation of the
reference capture,
it imply the the actual stored data behave like (or is actually) a pointer.
Pointers can be default constructed.
Is that a way of saying that captures by reference would be bound to
non-existing objects? Here is an excerpt from [dcl.ref] that makes this
A reference shall be initialized to refer to a valid object or function.
[ Note: in particular, a null reference cannot exist in a well-defined
program, because the only way to create such a reference would be to bind
it to the “object” obtained by indirection through a null pointer, which
causes undefined behavior. As described in 9.6, a reference cannot be bound
directly to a bit-field. — end note ]
[end]
Interesting. So even if I propose a zero-intiialization for reference
capture types, it would conflict with this.
So let's exclude captures by reference from this proposal idea then.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91OPu1EAQzG28skcsk8uANoZ8MMz_U6n1e-jiFNmj3iwfag%40mail.gmail.com.
Ville Voutilainen
2016-05-24 15:02:52 UTC
Permalink
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
If the lambda has captures, it's either insane or impossible to
default-construct it.
Why? I do not see any justification or explanation of what is "insane" so
far.
If the lambda has captures, those captures have values. A default
constructed lambda
will not have those same values. It's questionable whether it is of
the same type, and my
answer to that question is no.
Post by Klaim - Joël Lamotte
I do not see why a =default; could not be used
to rely on capture's type default constructors, deleting the default
constructor if it would not be possible for a normal type either.
Sure it could, but the problem isn't as much of whether the captures
are default-constructible,
but of what values they should have.
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
Treating non-capturing lambdas differently would require motivation that hasn't
appeared thus far, at least not in proposal form.
I agree that both cases should have the exact same behaviour, but this is
not really my point.
That "agreement" is not quite right, because I'm not suggesting that
those cases should have
the exact same behavior.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUb1Yg18WXqbEW%2BoFmvK7hqNvfW8VmwjH_EbunkbaTpg%3DA%40mail.gmail.com.
Klaim - Joël Lamotte
2016-05-24 15:16:21 UTC
Permalink
Post by Klaim - Joël Lamotte
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the
arguments
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
constructors).
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
If the lambda has captures, it's either insane or impossible to
default-construct it.
Why? I do not see any justification or explanation of what is "insane" so
far.
If the lambda has captures, those captures have values. A default
constructed lambda
will not have those same values. It's questionable whether it is of
the same type, and my
answer to that question is no.
Why would captured types be of different types if you are using the same
closure type?
Post by Klaim - Joël Lamotte
Post by Klaim - Joël Lamotte
I do not see why a =default; could not be used
to rely on capture's type default constructors, deleting the default
constructor if it would not be possible for a normal type either.
Sure it could, but the problem isn't as much of whether the captures
are default-constructible,
but of what values they should have.
Is'nt default construction's value when available a reasonnable value?
Post by Klaim - Joël Lamotte
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
Treating non-capturing lambdas differently would require motivation that hasn't
appeared thus far, at least not in proposal form.
I agree that both cases should have the exact same behaviour, but this is
not really my point.
That "agreement" is not quite right, because I'm not suggesting that
those cases should have
the exact same behavior.
If default construction of captured types is used, then the behaviour is
uniform
and I do not consider non-capturing lambda expressions to be a special case.
This is what I meant. I'm not totally sure if there is a disagreement here.

Joël Lamotte
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91ONnAdJBRa%2BZEJ0e2VxTOAWbHN11gh2y58qCHKboqwJu7w%40mail.gmail.com.
Ville Voutilainen
2016-05-24 15:18:40 UTC
Permalink
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
If the lambda has captures, those captures have values. A default
constructed lambda
will not have those same values. It's questionable whether it is of
the same type, and my
answer to that question is no.
Why would captured types be of different types if you are using the same
closure type?
Because if the captured types have different values, the closure types
are not same, by design.
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
I do not see why a =default; could not be used
to rely on capture's type default constructors, deleting the default
constructor if it would not be possible for a normal type either.
Sure it could, but the problem isn't as much of whether the captures
are default-constructible,
but of what values they should have.
Is'nt default construction's value when available a reasonnable value?
No. And it doesn't work for reference captures anyway.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUa0NBwSSLf2sAdgdvyrkZ7LKDu60DgWYrcpK1h_b%3DHLJA%40mail.gmail.com.
Klaim - Joël Lamotte
2016-05-24 15:26:59 UTC
Permalink
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
If the lambda has captures, those captures have values. A default
constructed lambda
will not have those same values. It's questionable whether it is of
the same type, and my
answer to that question is no.
Why would captured types be of different types if you are using the same
closure type?
Because if the captured types have different values, the closure types
are not same, by design.
My understanding so far is not what you describe.
The closure type is always the same for one lambda expression,
but it's stored values can be whatever is possible values of the captured
types.
For example:

#include <typeindex>
#include <cassert>

template<class T>
auto make_func(T value)
{
return [=]{ return value; };
}


int main()
{
assert(typeid(make_func(42)) == typeid(make_func(1234))); // succeed
}


That is, I make a clear difference beween the lambda expression and the
generated
anonymous closure type, which is still a type.
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
I do not see why a =default; could not be used
to rely on capture's type default constructors, deleting the default
constructor if it would not be possible for a normal type either.
Sure it could, but the problem isn't as much of whether the captures
are default-constructible,
but of what values they should have.
Is'nt default construction's value when available a reasonnable value?
No. And it doesn't work for reference captures anyway.
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUa0NBwSSLf2sAdgdvyrkZ7LKDu60DgWYrcpK1h_b%3DHLJA%40mail.gmail.com
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91OOZsRXgx5eGG4ef8KsoaWjQm5xcbdzwqB1S1Csm9%3DQz_w%40mail.gmail.com.
Ville Voutilainen
2016-05-24 15:34:54 UTC
Permalink
Post by Klaim - Joël Lamotte
My understanding so far is not what you describe.
The closure type is always the same for one lambda expression,
but it's stored values can be whatever is possible values of the captured
types.
[expr.prim.lambda]/4:

"The type of the lambda-expression (which is also the type of the
closure object) is a unique, unnamed
non-union class type — called the closure type — whose properties are
described below."
Post by Klaim - Joël Lamotte
#include <typeindex>
#include <cassert>
template<class T>
auto make_func(T value)
{
return [=]{ return value; };
}
int main()
{
assert(typeid(make_func(42)) == typeid(make_func(1234))); // succeed
}
That looks like a bug to me. The lambdas returned from different
specializations of make_func are not
unique types.
Post by Klaim - Joël Lamotte
That is, I make a clear difference beween the lambda expression and the
generated
anonymous closure type, which is still a type.
The wording seems to disagree with you.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUa%3D8emeBYAP-3BHtvt%3DQiw3RDjwPEn4zsoHrsrv%2BreB1g%40mail.gmail.com.
Klaim - Joël Lamotte
2016-05-24 15:46:22 UTC
Permalink
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
My understanding so far is not what you describe.
The closure type is always the same for one lambda expression,
but it's stored values can be whatever is possible values of the captured
types.
"The type of the lambda-expression (which is also the type of the
closure object) is a unique, unnamed
non-union class type — called the closure type — whose properties are
described below."
Post by Klaim - Joël Lamotte
#include <typeindex>
#include <cassert>
template<class T>
auto make_func(T value)
{
return [=]{ return value; };
}
int main()
{
assert(typeid(make_func(42)) == typeid(make_func(1234))); // succeed
}
That looks like a bug to me. The lambdas returned from different
specializations of make_func are not
unique types.
I do not see how types could be generated for each possible values (which
depends
on the runtime). Or am I misunderstanding your point?
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
That is, I make a clear difference beween the lambda expression and the
generated
anonymous closure type, which is still a type.
The wording seems to disagree with you.
I read that the lambda expression is a unique unnamed closure type.
I do not read that it's type is relative to the values of the captured
objects.
However it does imply it's type it is relative to the _type_ of these
captured objects
and that the initial value of the instance of the closure type does have an
initial value relative
to the captured objects values.
Post by Ville Voutilainen
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUa%3D8emeBYAP-3BHtvt%3DQiw3RDjwPEn4zsoHrsrv%2BreB1g%40mail.gmail.com
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91OPXTvsphdwzEOFip9T4qfVwFUX52eE4%2BOhFNazZC9mAiA%40mail.gmail.com.
Ville Voutilainen
2016-05-24 16:04:44 UTC
Permalink
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
#include <typeindex>
#include <cassert>
template<class T>
auto make_func(T value)
{
return [=]{ return value; };
}
int main()
{
assert(typeid(make_func(42)) == typeid(make_func(1234))); // succeed
}
That looks like a bug to me. The lambdas returned from different
specializations of make_func are not
unique types.
I do not see how types could be generated for each possible values (which
depends
on the runtime). Or am I misunderstanding your point?
Argh, sorry, I completely misread the example. I see what you mean now.
Post by Klaim - Joël Lamotte
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
That is, I make a clear difference beween the lambda expression and the
generated
anonymous closure type, which is still a type.
The wording seems to disagree with you.
I read that the lambda expression is a unique unnamed closure type.
I do not read that it's type is relative to the values of the captured
objects.
Correct.
Post by Klaim - Joël Lamotte
However it does imply it's type it is relative to the _type_ of these
captured objects
and that the initial value of the instance of the closure type does have an
initial value relative
to the captured objects values.
Right. So, despite my momentary adventures to the crazy-land, we have
three issues here:

1) currently, it's not possible for a capturing lambda to have
captures that have default-constructed
values, they are the result of copy-initialization. Changing that is a
breaking change, because some
lunatic might rely on that. I don't have practical examples of that.

2) reference-capturing lambdas can't be default-constructed, nor can
lambdas which capture
non-default-constructible types. So not all lambdas can be
default-constructed. That doesn't seem
to be a huge issue. Not all lambdas can be copied, either, as that
won't work with captures of move-only
type.

3) for lambdas that don't depend on run-time values, users can
currently rely on the lambda only ever
having the values that were captured when the lambda was created.
Allowing default-construction with
some other values breaks that expectation. Again, I don't know whether
that's significant.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAFk2RUYPgSeJDAXh8_%3DML92%3Dhr0naDSFbwooFSpfRoMDUJtMBg%40mail.gmail.com.
Viacheslav Usov
2016-05-24 15:59:32 UTC
Permalink
On Tue, May 24, 2016 at 5:34 PM, Ville Voutilainen <
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
template<class T>
auto make_func(T value)
{
return [=]{ return value; };
}
int main()
{
assert(typeid(make_func(42)) == typeid(make_func(1234))); // succeed
}
That looks like a bug to me. The lambdas returned from
different specializations of make_func are not unique types.
I do not see different specializations here. make_func<int> is used in both
cases.

I do not think I can follow the argument of either of you at this point,
though. The original proposal has already been reduced to
capture-by-value-only lambdas. Making that default constructible,
contingent on default constructibility of the captured types, should be
rather trivial. The question is, is there any really useful effect
achievable through this and not achievable otherwise that is worth the
effort? I am not convinced.

Cheers,
V.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg2%3D1yOuD-w78g%2BX5SrjaNfaHh3Sp%3DfKi%2Btgb5p5DpTn8A%40mail.gmail.com.
Nicola Gigante
2016-05-24 16:06:32 UTC
Permalink
Post by Ville Voutilainen
Post by Klaim - Joël Lamotte
My understanding so far is not what you describe.
The closure type is always the same for one lambda expression,
but it's stored values can be whatever is possible values of the captured
types.
"The type of the lambda-expression (which is also the type of the
closure object) is a unique, unnamed
non-union class type — called the closure type — whose properties are
described below."
Post by Klaim - Joël Lamotte
#include <typeindex>
#include <cassert>
template<class T>
auto make_func(T value)
{
return [=]{ return value; };
}
int main()
{
assert(typeid(make_func(42)) == typeid(make_func(1234))); // succeed
}
That looks like a bug to me. The lambdas returned from different
specializations of make_func are not
unique types.
Post by Klaim - Joël Lamotte
That is, I make a clear difference beween the lambda expression and the
generated
anonymous closure type, which is still a type.
The wording seems to disagree with you.
For what matters for this thread, the example can be rephrased
without templates, since it is calling the same instantiation of the
function anyway. If you change T, the type will be different, indeed.


Consider:

auto make_func(int value)
{
return [=]{ return value; };
}

Then call make_func(42) and make_func(1337).
How can you expect the lambda to change its type?
The captured value is a runtime effect of the execution of the program,
it cannot affect the type in this way. The return type of make_func
has to be always the same by definition. Functions have a well-defined type.

Klaim’s claim (eheh) is correct. Types of lambda functions are uniquely determined
independently from the captured *values*.

To return to the main issue, in my opinion it is perfectly sensible to provide
a default constructor to the closure type *when a user defined type with the
same members of the same type as captures would be able to have one*.

E.g.:

struct NC { NC(int x); };

{
int x;
NC nc{42};

auto f = [x]{ return x; };
auto g = [nc]{ return nc; };
}

In this case, decltype(f) should have a default constructor because ‘int’ can be default
constructed, while decltype(g) should not, because the NC struct is not default constructible.

It is perfectly coherent with how closure types are translated under the hood:

struct f_lambda_t
{
auto operator()() const {
return x;
}
private:
int x;
};

struct g_lambda_t
{
auto operator()() const {
return x;
}
private:
NC x;
};

If you add f_lambda_t() = default; and g_lambda_t() = default;, respectively, you
obtain what the original email was asking. For user defined types it would be perfectly
legal and well-defined. Also, someone brought up the issue about reference captures.
There is no issue. Put a reference in the struct above and the default constructor would
not be generated.

In a generic context, to me it is very annoying that closure types are not regular when
all the captured types are regular.

Bye,
Nicola
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/7507DCF8-0FC3-4E9B-ABC6-FBCEF91381D8%40gmail.com.
Nevin Liber
2016-05-24 16:13:05 UTC
Permalink
Post by Nicola Gigante
To return to the main issue, in my opinion it is perfectly sensible to provide
a default constructor to the closure type *when a user defined type with the
same members of the same type as captures would be able to have one*.
Why? No one has yet provided motivation for this, and given that it is
fairly painful to get the type of the lambda to declare another one (you
need to pass it to a template or decltype), having a capturing lambda where
the values are either the ones captured or ones that are default
constructed doesn't seem all that useful.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> +1-847-691-1404
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BMNO%3DifeLz%2BTmNdkyJ33GH10Sn_pGcrfcy0j7V%2B94B4hQ%40mail.gmail.com.
Klaim - Joël Lamotte
2016-05-24 16:31:49 UTC
Permalink
Post by Nevin Liber
Post by Nicola Gigante
To return to the main issue, in my opinion it is perfectly sensible to provide
a default constructor to the closure type *when a user defined type with the
same members of the same type as captures would be able to have one*.
Why? No one has yet provided motivation for this, and given that it is
fairly painful to get the type of the lambda to declare another one (you
need to pass it to a template or decltype), having a capturing lambda where
the values are either the ones captured or ones that are default
constructed doesn't seem all that useful.
By "No one has yet provided motivation for this" do you mean that the
initial example is not representative
of usual generic code development and usage?
Post by Nevin Liber
--
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BMNO%3DifeLz%2BTmNdkyJ33GH10Sn_pGcrfcy0j7V%2B94B4hQ%40mail.gmail.com
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BMNO%3DifeLz%2BTmNdkyJ33GH10Sn_pGcrfcy0j7V%2B94B4hQ%40mail.gmail.com?utm_medium=email&utm_source=footer>
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91OM0r7NvGRgSZERaZSWePg9b%2B6bu%2B2SEAN7CzVywOQpfag%40mail.gmail.com.
Nevin Liber
2016-05-24 16:33:17 UTC
Permalink
Post by Klaim - Joël Lamotte
Post by Nevin Liber
Post by Nicola Gigante
To return to the main issue, in my opinion it is perfectly sensible to provide
a default constructor to the closure type *when a user defined type with the
same members of the same type as captures would be able to have one*.
Why? No one has yet provided motivation for this, and given that it is
fairly painful to get the type of the lambda to declare another one (you
need to pass it to a template or decltype), having a capturing lambda where
the values are either the ones captured or ones that are default
constructed doesn't seem all that useful.
By "No one has yet provided motivation for this" do you mean that the
initial example is not representative
of usual generic code development and usage?
I can't imagine generic code where I want an object in only one of two
states, one of which I have very little control in setting up.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> +1-847-691-1404
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BMQGffx53YLsD889EQBowJsq-wDhmNdV9OQb%3DkvgNthrQ%40mail.gmail.com.
j***@aldebaran.com
2016-05-24 15:31:35 UTC
Permalink
Since user-defined types with reference members are not defaut
constructible, by-reference capturing lambdas should not be
default-constructible, I guess.
Concerning values of default-constructed members, for example :

// We're in a generic context and F is the type of a lambda.
F f; // should default construct members, so undefined value for integral
types and pointers
F f{}; // should zero-initialize members (by constructing members with {})

Jeremy
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
Otherwise I would like to propose to add
ClosureType() = default;
to the definition of the closure generated by a lambda, if it's not
already proposed somewhere.
This would help working with closures in generic context where we assume
that the callable type is regular (it's not enough but it helps);
template< class T, class Callable >
struct FatForwardIterator
{
T value;
Callable incr; // deactivate the default constructor if it's a closure
//... etc
FatForwardIterator& operator++()
{
// ...
value = incr(value);
return *this;
}
};
// let's assume that the proposal to generate default comparisons is on...
template< class T, class Callable >
auto make_fat_iterator( T initial_value, Callable incr )
{
return FatForwardIterator<T, Callable>{ initial_value, incr };
}
template< class IteratorType>
void some_algorithm( IteratorType begin, IteratorType end )
{
IteratorType previous; // error: IteratorType do not have a default
constructor.
do
{
previous = begin;
++begin;
// ... use previous
}
while( begin != end );
}
void foo()
{
auto times2 = [](auto value){ return value * 2; };
auto begin = make_fat_iterator( 1, times2 );
auto end = make_fat_iterator( 8, times2 );
// ...
some_algorithm( begin, end ); // fails to compile
}
--
*This email and any attachment thereto are confidential and intended solely
for the use of the individual or entity to whom they are addressed.If you
are not the intended recipient, please be advised that disclosing, copying,
distributing or taking any action in reliance on the contents of this email
is strictly prohibited. In such case, please immediately advise the sender,
and delete all copies and attachment from your system.This email shall not
be construed and is not tantamount to an offer, an acceptance of offer, or
an agreement by SoftBank Robotics Europe on any discussion or contractual
document whatsoever. No employee or agent is authorized to represent or
bind SoftBank Robotics Europe to third parties by email, or act on behalf
of SoftBank Robotics Europe by email, without express written confirmation
by SoftBank Robotics Europe’ duly authorized representatives.*
------------------------------




*Ce message électronique et éventuelles piÚces jointes sont confidentiels,
et exclusivement destinés à la personne ou l'entité à qui ils sont
adressés.Si vous n'êtes pas le destinataire visé, vous êtes prié de ne pas
divulguer, copier, distribuer ou prendre toute décision sur la foi de ce
message électronique. Merci d'en aviser immédiatement l'expéditeur et de
supprimer toutes les copies et éventuelles piÚces jointes de votre
systÚme.Ce message électronique n'équivaut pas à une offre, à une
acceptation d’offre, ou à un accord de SoftBank Robotics Europe sur toute
discussion ou document contractuel quel qu’il soit, et ne peut être
interprété comme tel. Aucun employé ou agent de SoftBank Robotics Europe
n'est autorisé à représenter ou à engager la société par email, ou à agir
au nom et pour le compte de la société par email, sans qu’une confirmation
écrite soit donnée par le représentant légal de SoftBank Robotics Europe ou
par toute autre personne ayant reçu délégation de pouvoir appropriée.*
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/dcd439ad-d758-4765-a37d-45688f719fc3%40isocpp.org.
Viacheslav Usov
2016-05-24 16:17:54 UTC
Permalink
Post by Klaim - Joël Lamotte
IteratorType previous; // error: IteratorType do not have a default
constructor.

This is your real problem. Not that lambdas are not default constructible.
Your algorithm requires a default constructible iterator. This is not a
general requirement for iterators. I am sure there is a way to have a
default constructible iterator even if encapsulates something not default
constructible.

I do not see how this can motivate your proposal.

Cheers,
V.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg1P0SQz0PSKuDPwVF3i5PibasFEADoBGao4kkB92rJf%3DQ%40mail.gmail.com.
Klaim - Joël Lamotte
2016-05-24 16:29:44 UTC
Permalink
Post by Klaim - Joël Lamotte
Post by Klaim - Joël Lamotte
IteratorType previous; // error: IteratorType do not have a default
constructor.
This is your real problem. Not that lambdas are not default constructible.
Your algorithm requires a default constructible iterator. This is not a
general requirement for iterators. I am sure there is a way to have a
default constructible iterator even if encapsulates something not default
constructible.
I do not see how this can motivate your proposal.
Well, this example is directly inspired by Stepanov's book "Element's of
Programming", page 106 (find_adjacent_mismatch_forward()),
where such a default initialization seems necessary.

Joël Lamotte
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91ONLXF6TMOPoKoELR_L6oF3j-dn%3DvmrbwCByPs1HaP8PvA%40mail.gmail.com.
j***@aldebaran.com
2016-05-24 16:37:15 UTC
Permalink
Post by Klaim - Joël Lamotte
Post by Klaim - Joël Lamotte
Post by Klaim - Joël Lamotte
IteratorType previous; // error: IteratorType do not have a default
constructor.
This is your real problem. Not that lambdas are not default
constructible. Your algorithm requires a default constructible iterator.
This is not a general requirement for iterators. I am sure there is a way
to have a default constructible iterator even if encapsulates something not
default constructible.
I do not see how this can motivate your proposal.
Well, this example is directly inspired by Stepanov's book "Element's of
Programming", page 106 (find_adjacent_mismatch_forward()),
where such a default initialization seems necessary.
Note that in "Elements of programming" such a default constructed value is
said to be in a "partially-formed state", i.e. it's only assignable or
destructible.
So this means that it doesn't matter if the members are default-constructed
with an undefined value. The main purpose is to have a virtually free
default-construction.
Post by Klaim - Joël Lamotte
Joël Lamotte
--
*This email and any attachment thereto are confidential and intended solely
for the use of the individual or entity to whom they are addressed.If you
are not the intended recipient, please be advised that disclosing, copying,
distributing or taking any action in reliance on the contents of this email
is strictly prohibited. In such case, please immediately advise the sender,
and delete all copies and attachment from your system.This email shall not
be construed and is not tantamount to an offer, an acceptance of offer, or
an agreement by SoftBank Robotics Europe on any discussion or contractual
document whatsoever. No employee or agent is authorized to represent or
bind SoftBank Robotics Europe to third parties by email, or act on behalf
of SoftBank Robotics Europe by email, without express written confirmation
by SoftBank Robotics Europe’ duly authorized representatives.*
------------------------------




*Ce message électronique et éventuelles piÚces jointes sont confidentiels,
et exclusivement destinés à la personne ou l'entité à qui ils sont
adressés.Si vous n'êtes pas le destinataire visé, vous êtes prié de ne pas
divulguer, copier, distribuer ou prendre toute décision sur la foi de ce
message électronique. Merci d'en aviser immédiatement l'expéditeur et de
supprimer toutes les copies et éventuelles piÚces jointes de votre
systÚme.Ce message électronique n'équivaut pas à une offre, à une
acceptation d’offre, ou à un accord de SoftBank Robotics Europe sur toute
discussion ou document contractuel quel qu’il soit, et ne peut être
interprété comme tel. Aucun employé ou agent de SoftBank Robotics Europe
n'est autorisé à représenter ou à engager la société par email, ou à agir
au nom et pour le compte de la société par email, sans qu’une confirmation
écrite soit donnée par le représentant légal de SoftBank Robotics Europe ou
par toute autre personne ayant reçu délégation de pouvoir appropriée.*
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/5b592591-ef29-4ef2-bb6a-a93c4f6208dd%40isocpp.org.
Viacheslav Usov
2016-05-24 16:39:33 UTC
Permalink
Post by Klaim - Joël Lamotte
Well, this example is directly inspired by Stepanov's book "Element's of
Programming", page 106 (find_adjacent_mismatch_forward()), where such a
default initialization seems necessary.

Again. I cannot see how that motivates a need for default constructible
lambdas. Lambda is not a forward iterator, and it does not need to be one.
If you want to wrap a lambda in an forward iterator, you can do so
*without* default
constructibility of lambdas.

Cheers,
V.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg39K7vsB%2BzZP2ytZoMaHim_BbjFc0uLVEdNjd0F%2BkztXA%40mail.gmail.com.
j***@aldebaran.com
2016-05-24 17:23:50 UTC
Permalink
The initial example is, well, just an example.
The real motivation is to be able to manipulate lambdas as a Regular values.
It should be easy to use lambdas and compose them with the rest of the
language.
For example, if I design an new type and want it to be comparable (==), its
members should be comparable. If I want this new type to be default
constructible (for example, if I want to store instances in an array, or be
able to cheaply construct objects before having their values), its members
should be default constructible.
If I'm doing generic programming and one of the members of this new type is
an instance of a Callable type, there's no way for me to know if it's a
lambda or user-defined type (and from a generic point of view, I don't
care).
But the problem is, if this callable member is a user-defined type and it's
default-constructible, the equivalent lambda won't.

Example:

// This is default constructible (if M is default-constructible).
template<typename M>
struct udf_incr {
M m;
M operator()(M const& n) const {return n * m;}
};

int m{2};
// This is not.
auto lambda_incr = [=](auto n) {return n * m;}

// concepts: Regular T, RegularFunction<T (T)> F
template<typename T, typename F>
struct advance_t {
// advance_t is default constructible if T and F are
default-constructible.
T value;
F incr_fun;
void operator()() {
value = incr_fun(value);
}
};

Now, advance_t<int, udf_incr<int>> is default constructible, but
advance_t<int, decltype(lambda_incr)> is not, even if they are semantically
equivalent.
The same problem occurs with comparison if I add operator== to udf_incr.

The point is, in generic code it's not possible to use lambdas with types
designed to be Regular, and it seems feasible to correct this.
Post by Viacheslav Usov
Post by Klaim - Joël Lamotte
Well, this example is directly inspired by Stepanov's book "Element's of
Programming", page 106 (find_adjacent_mismatch_forward()), where such a
default initialization seems necessary.
Again. I cannot see how that motivates a need for default constructible
lambdas. Lambda is not a forward iterator, and it does not need to be one.
If you want to wrap a lambda in an forward iterator, you can do so
*without* default constructibility of lambdas.
Cheers,
V.
--
*This email and any attachment thereto are confidential and intended solely
for the use of the individual or entity to whom they are addressed.If you
are not the intended recipient, please be advised that disclosing, copying,
distributing or taking any action in reliance on the contents of this email
is strictly prohibited. In such case, please immediately advise the sender,
and delete all copies and attachment from your system.This email shall not
be construed and is not tantamount to an offer, an acceptance of offer, or
an agreement by SoftBank Robotics Europe on any discussion or contractual
document whatsoever. No employee or agent is authorized to represent or
bind SoftBank Robotics Europe to third parties by email, or act on behalf
of SoftBank Robotics Europe by email, without express written confirmation
by SoftBank Robotics Europe’ duly authorized representatives.*
------------------------------




*Ce message électronique et éventuelles piÚces jointes sont confidentiels,
et exclusivement destinés à la personne ou l'entité à qui ils sont
adressés.Si vous n'êtes pas le destinataire visé, vous êtes prié de ne pas
divulguer, copier, distribuer ou prendre toute décision sur la foi de ce
message électronique. Merci d'en aviser immédiatement l'expéditeur et de
supprimer toutes les copies et éventuelles piÚces jointes de votre
systÚme.Ce message électronique n'équivaut pas à une offre, à une
acceptation d’offre, ou à un accord de SoftBank Robotics Europe sur toute
discussion ou document contractuel quel qu’il soit, et ne peut être
interprété comme tel. Aucun employé ou agent de SoftBank Robotics Europe
n'est autorisé à représenter ou à engager la société par email, ou à agir
au nom et pour le compte de la société par email, sans qu’une confirmation
écrite soit donnée par le représentant légal de SoftBank Robotics Europe ou
par toute autre personne ayant reçu délégation de pouvoir appropriée.*
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6a390bde-e9e1-4066-b515-892d88716932%40isocpp.org.
Nicol Bolas
2016-05-24 17:55:03 UTC
Permalink
Post by j***@aldebaran.com
The initial example is, well, just an example.
The real motivation is to be able to manipulate lambdas as a Regular values.
I think the question you're missing is "why do you want to?"

If you have a function which takes a function object to be called later,
why would you want to manipulate such parameters "as a Regular values"?

Functions are *not* regular values. While some callable types may fit the
Regular concept, they do *not* fit the actual *semantics* behind that
concept.

Also, let's say I write this:

auto val = 30
auto lamb = [val]() {...}

That `...` part currently can be written under the assumption that the
captured `val` will *always* be 30. However, under your rules, it won't. It
may be 30, or it may be a default-constructed `int` (ie: undefined).

That's bad.

Sure, this example is rather simplistic. But `val` could be anything. It
could be a raw pointer for the lambda to access. Those will not produce
reasonable results under default construction.

The whole point of making lambdas a closure is that what they close over is
well-defined. By allowing lambdas to be default constructed, the values
they close over are no longer well-defined.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/9c3baf8c-5b47-4c1d-afa6-6852888daa94%40isocpp.org.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2016-05-24 18:12:06 UTC
Permalink
Post by Nicol Bolas
Post by j***@aldebaran.com
The initial example is, well, just an example.
The real motivation is to be able to manipulate lambdas as a Regular values.
I think the question you're missing is "why do you want to?"
If you have a function which takes a function object to be called later,
why would you want to manipulate such parameters "as a Regular values"?
Functions are *not* regular values. While some callable types may fit the
Regular concept, they do *not* fit the actual *semantics* behind that
concept.
Not to get side-tracked, but function objects can indeed be Regular and
meet all of the semantic requirements of Regular types. This is often the
case. Function pointers are Regular as are many user-defined function
objects. Which are the semantic requirements that you claim function
objects cannot meet? operator() is no different from any other kind of
associated function of a concept and its existence as a requirement does
not have any kind of effect on the ability for a model to be Regular. If
this is unclear, just change the name of the associated function from
"operator()" to "foo". Would you say that a concept with an associated
function called "foo" cannot be Regular? Assuming you agree that such a
type can be Regular, why would simply naming the function "operator()"
instead of "foo" change anything?

That said, I think default construction as a requirement for Regular types
was a mistake anyway because it's not really a requirement for any
algorithms, it's just a convenience. Still it is often useful for
convenience.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CANh8DE%3Dr30WfPaJOmOisdEStp2tf3sCs0qBwMh9ig9Kyxzpqfg%40mail.gmail.com.
Viacheslav Usov
2016-05-24 18:02:12 UTC
Permalink
Post by j***@aldebaran.com
The real motivation is to be able to manipulate lambdas as a Regular values.
This is a very general statement. Just having default constructibility
won't do it. And unless you (or somebody else) make a consistent proposal
for lambdas as fully regular values, the value of default constructibility *per
se* is questionable. It may be that C++ will *never* have the other things
that make lambdas fully regular.

Cheers,
V.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAA7YVg1dpj7Hnm-ksvsgDWrAjGGfWpkeE5A3unuQEx3pibnLpA%40mail.gmail.com.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2016-05-24 18:00:23 UTC
Permalink
Post by Viacheslav Usov
Again. I cannot see how that motivates a need for default constructible
lambdas.
It's never strictly a need, it's just a convenience.

It can also be argued that it is consistent with user-defined class types
for any lambda where a corresponding type that had the capture types as
datamembers would be a default constructible type (i.e. not for reference
types, etc.), as was mentioned. I would personally say that it makes more
sense to start with what's consistent with the rest of the language and
only diverge from that if there is some kind of logical inconsistency. In
other words, when adding a new feature such as lambdas (a little late now,
I know), the burden, IMO, should be on *opponents* to argue why there
should *not* be consistent rules with other types, as opposed to the
proposal author needing to provide motivating cases for consistency.
Language consistency should be a starting point because when features
differ, those are more differences that users need to know about, making
the language harder to learn and intuit (and in practice, it usually makes
it harder to write sensible generic code).

FWIW, even though I lean towards more consistency with other class types, I
have only personally found default-construction for captureless lambdas to
be desirable in my particular use-cases, and that's all that I personally
care for. Perhaps this is a more compelling example:

// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());

Of course, you can't use a lambda in an unevaluated context currently, nor
can one directly appear in a template argument, though there is/will be a
pre-Oulu proposal to allow these to take place (leaving in the originally
intended restriction that they do not appear in function declarations,
IIRC). Even without that proposal there are similar uses, though they are a
little less compelling since they aren't simple one-liners.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CANh8DEk2i97m%2BtRrTdJ4_sjq7xn8u7LRWLo0a6_HrUQhUw4KKg%40mail.gmail.com.
Nevin Liber
2016-05-24 18:10:38 UTC
Permalink
On 24 May 2016 at 13:00, 'Matt Calabrese' via ISO C++ Standard - Future
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
FWIW, even though I lean towards more consistency with other class types,
I have only personally found default-construction for captureless lambdas
to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
Not particularly, as captureless lambdas are a different beast, because
there is no difference between a copy constructed one and a default
constructed one, as they have no state.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> +1-847-691-1404
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BPuOaJVAhFemXvBW%3DxZxGa0%3DPXPeWwupzqaaF%2B8NNturQ%40mail.gmail.com.
'Matt Calabrese' via ISO C++ Standard - Future Proposals
2016-05-24 18:31:05 UTC
Permalink
Post by Nevin Liber
On 24 May 2016 at 13:00, 'Matt Calabrese' via ISO C++ Standard - Future
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
FWIW, even though I lean towards more consistency with other class
types, I have only personally found default-construction for captureless
lambdas to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
Not particularly, as captureless lambdas are a different beast, because
there is no difference between a copy constructed one and a default
constructed one, as they have no state.
I'm not sure what the "not particularly" is referring to here. Are you
saying that this is *not* a compelling example for default construction of
a lambda? I agree that stateless lambdas are different in that they don't
really raise any questions about whether or not the default-constructed
value is useful. I personally only really care about allowing
default-construction for captureless lambdas because those are the only
cases that I personally encounter. Other similar motivating examples for
default-constructible captureless lambdas are comparator functions for
associative containers, etc..

I was only talking more generally with respect to default-construction of
lambdas that have captures just on the principle that consistency should be
considered important by default whenever proposing new language features,
and that opponents should be the ones expected to have to argue *against*
consistency if it's somehow a problem. I understand that starting with
consistency is not an opinion that all people share, but it's useful to
realize that that is where some people are coming from in discussions like
this, which some people miss.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CANh8DE%3De_r0Aij8q1d1OM5TsnSppXogKAMGAhSnLNOsMr9bABg%40mail.gmail.com.
Nevin Liber
2016-05-24 18:55:56 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
// Use a lambda as a stateless deleter function
Post by Nevin Liber
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
Not particularly, as captureless lambdas are a different beast, because
there is no difference between a copy constructed one and a default
constructed one, as they have no state.
I'm not sure what the "not particularly" is referring to here.
It doesn't address the capturing lambda case.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Are you saying that this is *not* a compelling example for default
construction of a lambda?
It's a decent use case, although you have to change other parts of the
language to make it work (you cannot have a lambda expression in an
unevaluated operand).

Most uses I've seen for unique_ptr tend to be as members of a class, but
putting a lambda in a header makes it way too easy for an accidental ODR
violation. Other use cases, such as calling factory functions, are solved
by decltype and auto.
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
I was only talking more generally with respect to default-construction of
lambdas that have captures just on the principle that consistency should be
considered important by default whenever proposing new language features,
and that opponents should be the ones expected to have to argue *against*
consistency if it's somehow a problem. I understand that starting with
consistency is not an opinion that all people share, but it's useful to
realize that that is where some people are coming from in discussions like
this, which some people miss.
Whenever consistency is brought up, the obvious question is "consistency
with what?" "Regular type" is not a concept in the standard.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> +1-847-691-1404
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BP9%2BOn1enK3zK5JoqOeX94cugL9m-YBphc0q2QHfLQofA%40mail.gmail.com.
Arthur O'Dwyer
2016-05-24 23:31:42 UTC
Permalink
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Viacheslav Usov
Again. I cannot see how that motivates a need for default constructible
lambdas.
It's never strictly a need, it's just a convenience.
[...]
FWIW, even though I lean towards more consistency with other class types,
I have only personally found default-construction for captureless lambdas
to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
This is a use-case I have also encountered in the wild. I would very much
like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.

Default-construction of *captureful* close types seems 100% dangerous and
bad.

int main() {
int y = 42;
auto x = [z=y](){ printf("%d\n", z); };
decltype(x) x2;
x2(); // does this print the "value" of a default-initialized int?
that's super dangerous!
}

We definitely shouldn't provide the user-programmer with any constructs
that allow him to shoot himself in the foot this badly, especially when
there's no motivating use-case for the feature.

I would say that there's one minorly thought-provoking corner case in
between "captureless" and "captureful":

int main() {
auto x = [z=42](){ printf("%d\n", z); };
decltype(x) x2; // ought this to compile?
x2(); // ought this to print 42?
}

My opinion is that "no, it certainly oughtn't to print 42", based on my
(correct?) belief that the meaning of [z=FOO] is not altered in any way by
whether or not FOO is a constant-expression. In my ideal world, the above
code would *refuse to compile*, because decltype(x) is quite definitely a
*captureful* closure type.

Captureless closure types are already semantically more featureful than
captureful closure types; namely, they provide an implicit conversion to
function-pointer type. Providing a default constructor for captureless
closure types would be logically in keeping with that existing behavior,
IMO.

I see that existing behavior of captureless closure types as basically
analogous to the way each new C++ standard adds new accessors to
std::true_type:
- ::value
- default constructor + operator bool
- default constructor + operator()
It's convenient to be able to use std::true_type as if it "means" true,
even though technically they're different things.

For captureless closure types, we're only at the start of that journey. So
far we've added only
- operator <function-type>
but I think it makes sense to add
- default constructor
as well. This will enable user-programmers to more conveniently use
captureless closure types as if they "mean" functions, even though
technically they're different things.

–Arthur
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/e0838864-bcaf-4bc6-84c6-fa0cd1f3ee3f%40isocpp.org.
Nicol Bolas
2016-05-25 00:02:12 UTC
Permalink
Post by Arthur O'Dwyer
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Viacheslav Usov
Again. I cannot see how that motivates a need for default constructible
lambdas.
It's never strictly a need, it's just a convenience.
[...]
FWIW, even though I lean towards more consistency with other class types,
I have only personally found default-construction for captureless lambdas
to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
This is a use-case I have also encountered in the wild. I would very much
like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.
Default-construction of *captureful* close types seems 100% dangerous and
bad.
int main() {
int y = 42;
auto x = [z=y](){ printf("%d\n", z); };
decltype(x) x2;
x2(); // does this print the "value" of a default-initialized
int? that's super dangerous!
}
We definitely shouldn't provide the user-programmer with any constructs
that allow him to shoot himself in the foot this badly, especially when
there's no motivating use-case for the feature.
I would say that there's one minorly thought-provoking corner case in
int main() {
auto x = [z=42](){ printf("%d\n", z); };
decltype(x) x2; // ought this to compile?
x2(); // ought this to print 42?
}
My opinion is that "no, it certainly oughtn't to print 42", based on my
(correct?) belief that the meaning of [z=FOO] is not altered in any way
by whether or not FOO is a constant-expression. In my ideal world, the
above code would *refuse to compile*, because decltype(x) is quite
definitely a *captureful* closure type.
Captureless closure types are already semantically more featureful than
captureful closure types; namely, they provide an implicit conversion to
function-pointer type. Providing a default constructor for captureless
closure types would be logically in keeping with that existing behavior,
IMO.
I see that existing behavior of captureless closure types as basically
analogous to the way each new C++ standard adds new accessors to
- ::value
- default constructor + operator bool
- default constructor + operator()
It's convenient to be able to use std::true_type as if it "means" true,
even though technically they're different things.
For captureless closure types, we're only at the start of that journey. So
far we've added only
- operator <function-type>
but I think it makes sense to add
- default constructor
as well. This will enable user-programmers to more conveniently use
captureless closure types as if they "mean" functions, even though
technically they're different things.
–Arthur
That all sounds very reasonable. I particularly like not trying to create a
third class of lambdas which capture constants rather than external
variables.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/f89430ce-17a7-4efe-b604-38776cc6d0a0%40isocpp.org.
Nevin Liber
2016-05-25 01:33:21 UTC
Permalink
Post by Arthur O'Dwyer
This is a use-case I have also encountered in the wild. I would very much
like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.
Then how do you prevent:

#ifndef HEADER_H_
#define HEADER_H_

struct A
{
std::unique_ptr<B, decltype([](auto* p){delete_delete_delete(p);})> u;
std::set<C, decltype([](auto const&l, auto const& r){return l > r;})> s;
}

#endif /* HEADER_H_ */

from being an accidental ODR violation when someone includes this in two
translation units?
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> +1-847-691-1404
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BPWQaigKT2RejJ%3DWENs5-cCwYHqSNRkNfqz0YqRU%3DRyog%40mail.gmail.com.
Miro Knejp
2016-05-25 02:22:06 UTC
Permalink
Post by Arthur O'Dwyer
This is a use-case I have also encountered in the wild. I would
very much like this (lambdas in unevaluated contexts,
default-construction of captureless closure types) to Just Work.
#ifndef HEADER_H_
#define HEADER_H_
struct A
{
std::unique_ptr<B, decltype([](auto* p){delete_delete_delete(p);})> u;
std::set<C, decltype([](auto const&l, auto const& r){return l > r;})> s;
}
#endif /* HEADER_H_ */
from being an accidental ODR violation when someone includes this in
two translation units?
Clang uses file:line:column of the place of definition to distinguish
closure types regardless of translation unit. I see no reason why your
example should cause an ODR violation. It's just a matter of specifying
in the standard whether it should be one or not so it's not
implementation dependent.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ab7e1ab2-1558-23e6-8315-aff8d48dcc4e%40gmail.com.
Richard Smith
2016-05-25 07:09:16 UTC
Permalink
Post by Miro Knejp
Post by Nevin Liber
Post by Arthur O'Dwyer
This is a use-case I have also encountered in the wild. I would very
much like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.
Post by Miro Knejp
Post by Nevin Liber
#ifndef HEADER_H_
#define HEADER_H_
struct A
{
std::unique_ptr<B, decltype([](auto* p){delete_delete_delete(p);})> u;
std::set<C, decltype([](auto const&l, auto const& r){return l > r;})> s;
}
#endif /* HEADER_H_ */
from being an accidental ODR violation when someone includes this in two
translation units?
Post by Miro Knejp
Clang uses file:line:column of the place of definition to distinguish
closure types regardless of translation unit.

Well, actually, clang uses the mangling system for the target (typically
the vendor-neutral Itanium C++ ABI) to give the closure type the same name
across translation units; typically this involves sequentially numbering
the lambdas in each context where they can appear. (File:line:column would
not be correct since that is not required to be the same in each such
definition -- think about compiling preprocessed source for a practical
case where this happens.)
Post by Miro Knejp
I see no reason why your example should cause an ODR violation. It's just
a matter of specifying in the standard whether it should be one or not so
it's not implementation dependent.

The standard is already clear that it would not be: under the terms of the
odr, since you used the same token sequence to write the class definition
(and met the side conditions for lookup results), the implementation is
required to make your program act as if there is only one definition of the
class, that is merely somehow visible from multiple translation units at
once.
Post by Miro Knejp
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
Post by Miro Knejp
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ab7e1ab2-1558-23e6-8315-aff8d48dcc4e%40gmail.com
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOfiQqncfPKn9ekLWVH5uyB6y_iqBMt5f1EtQ7A4BT8equrhTw%40mail.gmail.com.
Arthur O'Dwyer
2016-05-26 19:02:34 UTC
Permalink
Post by Richard Smith
Post by Arthur O'Dwyer
This is a use-case I have also encountered in the wild. I would very
much like this
Post by Richard Smith
Post by Arthur O'Dwyer
(lambdas in unevaluated contexts, default-construction of captureless
closure types)
Post by Richard Smith
Post by Arthur O'Dwyer
to Just Work.
Then how do you prevent [...]
accidental ODR violation when someone includes this in two translation
units?
Post by Richard Smith
[...] I see no reason why your example should cause an ODR violation.
It's just a matter
Post by Richard Smith
of specifying in the standard whether it should be one or not so it's
not implementation
Post by Richard Smith
dependent.
The standard is already clear that it would not be: under the terms of
the odr, since you
Post by Richard Smith
used the same token sequence to write the class definition (and met the
side conditions
Post by Richard Smith
for lookup results), the implementation is required to make your program
act as if there
Post by Richard Smith
is only one definition of the class, that is merely somehow visible from
multiple translation
Post by Richard Smith
units at once.
Richard, could you expand on this a little bit, being as you're probably
the one here who knows the most about the ODR? :) It sounds to me as if
you're using basically a technicality (the existence of a surrounding
struct/class definition) to avoid the ODR in Nevin's specific case, but
that you haven't addressed the possibility of ODR violations or plain old
type mismatches in simpler cases.
For example, if we allowed decltype-of-lambda, would this program still be
ill-formed?:

cat >alpha.h <<EOF
EXTERN decltype([](){}) x;
EOF
cat >alpha.cc <<EOF
#define EXTERN extern
#include "alpha.h" // declare x of type T
#undef EXTERN
#define EXTERN
#include "alpha.h" // define x of type U; if U!=T, the program is
ill-formed, right?
int main() {}
EOF
clang++ alpha.cc

How about this one, where the initializer of an inline variable (does the
initializer count as part of the "definition" for purposes of the ODR?)
consists of the same sequence of tokens everywhere, but also contains an
implicit conversion from what I assume is a unique closure type, thus
apparently falling foul of N4567 3.2/(6.4)?:

cat >beta.cc <<EOF
inline int (*v)() = [](){ return 42; };
EOF
cat >gamma.cc <<EOF
inline int (*v)() = [](){ return 42; };
int main() {}
EOF
clang++ beta.cc gamma.cc

I mean, are you saying there are *no* lambda-related ODR problems, or are
the problems just *more subtle* than the one Nevin suggested? (And if the
only problems are *subtle*, isn't that kind of worse than if they were
right out in the open?)

Could any of the problems (if any) be addressed by adding one more special
rule to captureless lambdas: namely, that two captureless lambdas
consisting of the same sequence of tokens are guaranteed to behave as if
they correspond to the same definition of the lambda? That is, what if
tomorrow we made decltype([](){return 2;}) just as
translation-unit-invariant as decltype(2) is today? I think we have the
technology these days to make that happen (via the same mechanisms we use
for inlines and templates), although it might need a lot of work in the
mangler.

–Arthur
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CADvuK0K3x6RCFZE2UnGZGtpDSGjMZR1Y%2BvdksXVDHuFxNzJJMw%40mail.gmail.com.
Nevin Liber
2016-05-26 22:43:09 UTC
Permalink
\Richard, could you expand on this a little bit, being as you're probably
the one here who knows the most about the ODR? :) It sounds to me as if
you're using basically a technicality (the existence of a surrounding
struct/class definition) to avoid the ODR in Nevin's specific case, but
that you haven't addressed the possibility of ODR violations or plain old
type mismatches in simpler cases.
To be clear, my only real concern about extending captureless lambdas is
accidental ODR violations. If we can get past that, it would be a useful
feature.

I'm still firmly against extending capturing lambdas, as I just don't see a
compelling reason to do so.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> +1-847-691-1404
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BMkCo-vRqT1HTr0OOZZVKyfzJRfaqGYFTJ81ixr%2BuubbQ%40mail.gmail.com.
Richard Smith
2016-05-27 05:31:24 UTC
Permalink
Post by Arthur O'Dwyer
Post by Richard Smith
Post by Arthur O'Dwyer
This is a use-case I have also encountered in the wild. I would very
much like this
Post by Richard Smith
Post by Arthur O'Dwyer
(lambdas in unevaluated contexts, default-construction of
captureless closure types)
Post by Richard Smith
Post by Arthur O'Dwyer
to Just Work.
Then how do you prevent [...]
accidental ODR violation when someone includes this in two
translation units?
Post by Richard Smith
[...] I see no reason why your example should cause an ODR violation.
It's just a matter
Post by Richard Smith
of specifying in the standard whether it should be one or not so it's
not implementation
Post by Richard Smith
dependent.
The standard is already clear that it would not be: under the terms of
the odr, since you
Post by Richard Smith
used the same token sequence to write the class definition (and met the
side conditions
Post by Richard Smith
for lookup results), the implementation is required to make your program
act as if there
Post by Richard Smith
is only one definition of the class, that is merely somehow visible from
multiple translation
Post by Richard Smith
units at once.
Richard, could you expand on this a little bit, being as you're probably
the one here who knows the most about the ODR? :) It sounds to me as if
you're using basically a technicality (the existence of a surrounding
struct/class definition) to avoid the ODR in Nevin's specific case, but
that you haven't addressed the possibility of ODR violations or plain old
type mismatches in simpler cases.
It may be a technicality, but it's an important one that's reasonably
central to the rules that make class / inline function definitions in
header files work in C++. Here's another case of the same rule:

// foo.h
struct A {
struct { int x, y; } s;
};

decltype(A().s) is the same across translation units because of this rule.
And another:

// bar.h
inline auto f() {
return [] {};
}

f() can be defined in multiple translation units only because the lambda
has the same type in each instance.

For example, if we allowed decltype-of-lambda, would this program still be
Post by Arthur O'Dwyer
cat >alpha.h <<EOF
EXTERN decltype([](){}) x;
EOF
cat >alpha.cc <<EOF
#define EXTERN extern
#include "alpha.h" // declare x of type T
#undef EXTERN
#define EXTERN
#include "alpha.h" // define x of type U; if U!=T, the program is
ill-formed, right?
int main() {}
EOF
clang++ alpha.cc
Yes, each occurrence of the [](){} token sequence here would create a
distinct closure type; that would be ill-formed.

How about this one, where the initializer of an inline variable (does the
Post by Arthur O'Dwyer
initializer count as part of the "definition" for purposes of the ODR?)
consists of the same sequence of tokens everywhere, but also contains an
implicit conversion from what I assume is a unique closure type, thus
cat >beta.cc <<EOF
inline int (*v)() = [](){ return 42; };
EOF
cat >gamma.cc <<EOF
inline int (*v)() = [](){ return 42; };
int main() {}
EOF
clang++ beta.cc gamma.cc
That would be fine, just as it would if the lambda were instead defined
inside an inline function or class definition. The program behaves as if
there is exactly one definition of the variable.

I mean, are you saying there are *no* lambda-related ODR problems, or are
Post by Arthur O'Dwyer
the problems just *more subtle* than the one Nevin suggested? (And if the
only problems are *subtle*, isn't that kind of worse than if they were
right out in the open?)
The latter. Do you have a plausible example where the ODR would be violated
but no diagnostic would be produced? It seems likely to me that problems
would often look like your EXTERN case above and would be reliably
diagnosed, but maybe there are other kinds of likely failure mode.

Could any of the problems (if any) be addressed by adding one more special
Post by Arthur O'Dwyer
rule to captureless lambdas: namely, that two captureless lambdas
consisting of the same sequence of tokens are guaranteed to behave as if
they correspond to the same definition of the lambda? That is, what if
tomorrow we made decltype([](){return 2;}) just as
translation-unit-invariant as decltype(2) is today? I think we have the
technology these days to make that happen (via the same mechanisms we use
for inlines and templates), although it might need a lot of work in the
mangler.
That sounds pretty painful. But yes, that'd solve the problem :)
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOfiQqn-9qmO-jX-T6GWzQbyLuVP%2BoFLesenZA_jffe-rt9a%3DA%40mail.gmail.com.
j***@aldebaran.com
2016-05-25 09:10:28 UTC
Permalink
Post by Arthur O'Dwyer
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Viacheslav Usov
Again. I cannot see how that motivates a need for default constructible
lambdas.
It's never strictly a need, it's just a convenience.
[...]
FWIW, even though I lean towards more consistency with other class types,
I have only personally found default-construction for captureless lambdas
to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
This is a use-case I have also encountered in the wild. I would very much
like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.
Default-construction of *captureful* close types seems 100% dangerous and
bad.
int main() {
int y = 42;
auto x = [z=y](){ printf("%d\n", z); };
decltype(x) x2;
x2(); // does this print the "value" of a default-initialized
int? that's super dangerous!
}
We definitely shouldn't provide the user-programmer with any constructs
that allow him to shoot himself in the foot this badly, especially when
there's no motivating use-case for the feature.
See above for motivating examples.
Post by Arthur O'Dwyer
I would say that there's one minorly thought-provoking corner case in
int main() {
auto x = [z=42](){ printf("%d\n", z); };
decltype(x) x2; // ought this to compile?
x2(); // ought this to print 42?
}
My opinion is that "no, it certainly oughtn't to print 42", based on my
(correct?) belief that the meaning of [z=FOO] is not altered in any way
by whether or not FOO is a constant-expression. In my ideal world, the
above code would *refuse to compile*, because decltype(x) is quite
definitely a *captureful* closure type.
As explained above, the default constructor of the lambda should rely on
the default constructor of its members (same as a user-defined type). Thus,
the value of a default-constructed lambda would be meaningful depending of
the meaningfulness of the values of its default-constructed members.
For example, imagine a lambda capturing a std::string, the default
construction of the type of this lambda should default-constructs its
std::string member, which has a meaningful value (the empty string).
Therefore, the value of this default-constructed lambda type is meaningful.

For a lambda capturing an int :
decltype(my_lambda_capturing_an_int) x; // the int member's value is
undefined (members are default-constructed without empty braces)
decltype(my_lambda_capturing_an_int) x{}; // the int member's value is 0
(members are default-constructed with empty braces)

(though I don't remember if a type default-constructed with empty braces
will recursively default-construct its members with empty braces).

This way, default-construction of a lambda type is consistent with
user-defined types.
Post by Arthur O'Dwyer
Captureless closure types are already semantically more featureful than
captureful closure types; namely, they provide an implicit conversion to
function-pointer type. Providing a default constructor for captureless
closure types would be logically in keeping with that existing behavior,
IMO.
I see that existing behavior of captureless closure types as basically
analogous to the way each new C++ standard adds new accessors to
- ::value
- default constructor + operator bool
- default constructor + operator()
It's convenient to be able to use std::true_type as if it "means" true,
even though technically they're different things.
For captureless closure types, we're only at the start of that journey. So
far we've added only
- operator <function-type>
but I think it makes sense to add
- default constructor
as well. This will enable user-programmers to more conveniently use
captureless closure types as if they "mean" functions, even though
technically they're different things.
–Arthur
--
*This email and any attachment thereto are confidential and intended solely
for the use of the individual or entity to whom they are addressed.If you
are not the intended recipient, please be advised that disclosing, copying,
distributing or taking any action in reliance on the contents of this email
is strictly prohibited. In such case, please immediately advise the sender,
and delete all copies and attachment from your system.This email shall not
be construed and is not tantamount to an offer, an acceptance of offer, or
an agreement by SoftBank Robotics Europe on any discussion or contractual
document whatsoever. No employee or agent is authorized to represent or
bind SoftBank Robotics Europe to third parties by email, or act on behalf
of SoftBank Robotics Europe by email, without express written confirmation
by SoftBank Robotics Europe’ duly authorized representatives.*
------------------------------




*Ce message électronique et éventuelles piÚces jointes sont confidentiels,
et exclusivement destinés à la personne ou l'entité à qui ils sont
adressés.Si vous n'êtes pas le destinataire visé, vous êtes prié de ne pas
divulguer, copier, distribuer ou prendre toute décision sur la foi de ce
message électronique. Merci d'en aviser immédiatement l'expéditeur et de
supprimer toutes les copies et éventuelles piÚces jointes de votre
systÚme.Ce message électronique n'équivaut pas à une offre, à une
acceptation d’offre, ou à un accord de SoftBank Robotics Europe sur toute
discussion ou document contractuel quel qu’il soit, et ne peut être
interprété comme tel. Aucun employé ou agent de SoftBank Robotics Europe
n'est autorisé à représenter ou à engager la société par email, ou à agir
au nom et pour le compte de la société par email, sans qu’une confirmation
écrite soit donnée par le représentant légal de SoftBank Robotics Europe ou
par toute autre personne ayant reçu délégation de pouvoir appropriée.*
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/4b453658-5a0f-466e-80f1-6b7312e5bee6%40isocpp.org.
j***@aldebaran.com
2016-05-25 10:19:50 UTC
Permalink
Post by j***@aldebaran.com
Post by Arthur O'Dwyer
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Viacheslav Usov
Again. I cannot see how that motivates a need for default constructible
lambdas.
It's never strictly a need, it's just a convenience.
[...]
FWIW, even though I lean towards more consistency with other class
types, I have only personally found default-construction for captureless
lambdas to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
This is a use-case I have also encountered in the wild. I would very much
like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.
Default-construction of *captureful* close types seems 100% dangerous
and bad.
int main() {
int y = 42;
auto x = [z=y](){ printf("%d\n", z); };
decltype(x) x2;
x2(); // does this print the "value" of a default-initialized
int? that's super dangerous!
}
We definitely shouldn't provide the user-programmer with any constructs
that allow him to shoot himself in the foot this badly, especially when
there's no motivating use-case for the feature.
See above for motivating examples.
Post by Arthur O'Dwyer
I would say that there's one minorly thought-provoking corner case in
int main() {
auto x = [z=42](){ printf("%d\n", z); };
decltype(x) x2; // ought this to compile?
x2(); // ought this to print 42?
}
My opinion is that "no, it certainly oughtn't to print 42", based on my
(correct?) belief that the meaning of [z=FOO] is not altered in any way
by whether or not FOO is a constant-expression. In my ideal world, the
above code would *refuse to compile*, because decltype(x) is quite
definitely a *captureful* closure type.
As explained above, the default constructor of the lambda should rely on
the default constructor of its members (same as a user-defined type). Thus,
the value of a default-constructed lambda would be meaningful depending of
the meaningfulness of the values of its default-constructed members.
For example, imagine a lambda capturing a std::string, the default
construction of the type of this lambda should default-constructs its
std::string member, which has a meaningful value (the empty string).
Therefore, the value of this default-constructed lambda type is meaningful.
decltype(my_lambda_capturing_an_int) x; // the int member's value is
undefined (members are default-constructed without empty braces)
decltype(my_lambda_capturing_an_int) x{}; // the int member's value is 0
(members are default-constructed with empty braces)
One precision regarding the default construction without a meaningful
value, as in this case :
decltype(my_lambda_capturing_an_int) x;

The constructed value could be considered useless but again, following
Stepanov (Elements of Programming, p7), you can decide to have the default
construction of a type leaving the object in a *partially formed state*,
meaning you can only assign a value to it or destroy it. The main gain is
to have a free default construction by not initializing anything. See the
initial example for a use-case, or for example
find_adjacent_mismatch_forward (EoP, p106).
Post by j***@aldebaran.com
(though I don't remember if a type default-constructed with empty braces
will recursively default-construct its members with empty braces).
This way, default-construction of a lambda type is consistent with
user-defined types.
Post by Arthur O'Dwyer
Captureless closure types are already semantically more featureful than
captureful closure types; namely, they provide an implicit conversion to
function-pointer type. Providing a default constructor for captureless
closure types would be logically in keeping with that existing behavior,
IMO.
I see that existing behavior of captureless closure types as basically
analogous to the way each new C++ standard adds new accessors to
- ::value
- default constructor + operator bool
- default constructor + operator()
It's convenient to be able to use std::true_type as if it "means" true,
even though technically they're different things.
For captureless closure types, we're only at the start of that journey.
So far we've added only
- operator <function-type>
but I think it makes sense to add
- default constructor
as well. This will enable user-programmers to more conveniently use
captureless closure types as if they "mean" functions, even though
technically they're different things.
–Arthur
--
*This email and any attachment thereto are confidential and intended solely
for the use of the individual or entity to whom they are addressed.If you
are not the intended recipient, please be advised that disclosing, copying,
distributing or taking any action in reliance on the contents of this email
is strictly prohibited. In such case, please immediately advise the sender,
and delete all copies and attachment from your system.This email shall not
be construed and is not tantamount to an offer, an acceptance of offer, or
an agreement by SoftBank Robotics Europe on any discussion or contractual
document whatsoever. No employee or agent is authorized to represent or
bind SoftBank Robotics Europe to third parties by email, or act on behalf
of SoftBank Robotics Europe by email, without express written confirmation
by SoftBank Robotics Europe’ duly authorized representatives.*
------------------------------




*Ce message électronique et éventuelles piÚces jointes sont confidentiels,
et exclusivement destinés à la personne ou l'entité à qui ils sont
adressés.Si vous n'êtes pas le destinataire visé, vous êtes prié de ne pas
divulguer, copier, distribuer ou prendre toute décision sur la foi de ce
message électronique. Merci d'en aviser immédiatement l'expéditeur et de
supprimer toutes les copies et éventuelles piÚces jointes de votre
systÚme.Ce message électronique n'équivaut pas à une offre, à une
acceptation d’offre, ou à un accord de SoftBank Robotics Europe sur toute
discussion ou document contractuel quel qu’il soit, et ne peut être
interprété comme tel. Aucun employé ou agent de SoftBank Robotics Europe
n'est autorisé à représenter ou à engager la société par email, ou à agir
au nom et pour le compte de la société par email, sans qu’une confirmation
écrite soit donnée par le représentant légal de SoftBank Robotics Europe ou
par toute autre personne ayant reçu délégation de pouvoir appropriée.*
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/7e217e87-cf62-4a34-ac3c-727548dd5d86%40isocpp.org.
Nicol Bolas
2016-05-25 15:35:13 UTC
Permalink
Post by j***@aldebaran.com
Post by Arthur O'Dwyer
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Viacheslav Usov
Again. I cannot see how that motivates a need for default constructible
lambdas.
It's never strictly a need, it's just a convenience.
[...]
FWIW, even though I lean towards more consistency with other class
types, I have only personally found default-construction for captureless
lambdas to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
This is a use-case I have also encountered in the wild. I would very much
like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.
Default-construction of *captureful* close types seems 100% dangerous
and bad.
int main() {
int y = 42;
auto x = [z=y](){ printf("%d\n", z); };
decltype(x) x2;
x2(); // does this print the "value" of a default-initialized
int? that's super dangerous!
}
We definitely shouldn't provide the user-programmer with any constructs
that allow him to shoot himself in the foot this badly, especially when
there's no motivating use-case for the feature.
See above for motivating examples.
Post by Arthur O'Dwyer
I would say that there's one minorly thought-provoking corner case in
int main() {
auto x = [z=42](){ printf("%d\n", z); };
decltype(x) x2; // ought this to compile?
x2(); // ought this to print 42?
}
My opinion is that "no, it certainly oughtn't to print 42", based on my
(correct?) belief that the meaning of [z=FOO] is not altered in any way
by whether or not FOO is a constant-expression. In my ideal world, the
above code would *refuse to compile*, because decltype(x) is quite
definitely a *captureful* closure type.
As explained above, the default constructor of the lambda should rely on
the default constructor of its members (same as a user-defined type). Thus,
the value of a default-constructed lambda would be meaningful depending of
the meaningfulness of the values of its default-constructed members.
I don't think you're getting the point.

If a lambda captures something, then the lambda function is either written
with the expectation that the capture will have the exact captured value or
it is not. If the function expects the value, then a default constructed
value of that type is *wrong* and will cause the function to behave
inappropriately.

There is *no way* to distinguish between these two cases. As such, whether
it is valid to default construct a lambda that captures values is not known
and cannot be known. Therefore, permitting the default construction of a
capturing lambda creates fragile, brittle code.

That's bad.

This is like the rules of trivial copyability. It's possible to write
classes which violate the rules of trivial copyability yet still
technically could be. This is based on how the user-defined copy/move
operations work and what data is being stored. But despite this, we don't
allow trivially copying them to be well-defined C++ behavior. We only allow
it to be well-defined in circumstances where we can be *certain* that such
copying will be reasonable, where we can *statically* determine if it will
always be legitimate.

The same goes here. The only case where we can be certain that default
constructing a lambda will produce a valid lambda is if it is captureless.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/55208578-a1d4-443b-bc92-4c52729bb367%40isocpp.org.
j***@aldebaran.com
2016-05-26 10:14:55 UTC
Permalink
Post by Nicol Bolas
Post by j***@aldebaran.com
Post by Arthur O'Dwyer
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Viacheslav Usov
Again. I cannot see how that motivates a need for default
constructible lambdas.
It's never strictly a need, it's just a convenience.
[...]
FWIW, even though I lean towards more consistency with other class
types, I have only personally found default-construction for captureless
lambdas to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
This is a use-case I have also encountered in the wild. I would very
much like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.
Default-construction of *captureful* close types seems 100% dangerous
and bad.
int main() {
int y = 42;
auto x = [z=y](){ printf("%d\n", z); };
decltype(x) x2;
x2(); // does this print the "value" of a default-initialized
int? that's super dangerous!
}
We definitely shouldn't provide the user-programmer with any constructs
that allow him to shoot himself in the foot this badly, especially when
there's no motivating use-case for the feature.
See above for motivating examples.
Post by Arthur O'Dwyer
I would say that there's one minorly thought-provoking corner case in
int main() {
auto x = [z=42](){ printf("%d\n", z); };
decltype(x) x2; // ought this to compile?
x2(); // ought this to print 42?
}
My opinion is that "no, it certainly oughtn't to print 42", based on my
(correct?) belief that the meaning of [z=FOO] is not altered in any way
by whether or not FOO is a constant-expression. In my ideal world, the
above code would *refuse to compile*, because decltype(x) is quite
definitely a *captureful* closure type.
As explained above, the default constructor of the lambda should rely on
the default constructor of its members (same as a user-defined type). Thus,
the value of a default-constructed lambda would be meaningful depending of
the meaningfulness of the values of its default-constructed members.
I don't think you're getting the point.
If a lambda captures something, then the lambda function is either written
with the expectation that the capture will have the exact captured value or
it is not. If the function expects the value, then a default constructed
value of that type is *wrong* and will cause the function to behave
inappropriately.
I'm not sure to follow you.
If I write:

std::string suffix{"abcd"};
auto append = [=](std::string const& str) {return str + suffix;};

I expect the generated lambda type to be roughly equivalent to :

struct lambda_type {
std::string suffix;
lambda_type(std::string s) : suffix(std::move(s)) {}
auto operator()(std::string const& str) const {return str + suffix;}
};
// ...
lambda_type append{"abcd"};

Why adding a default constructor to lambda_type would be wrong ?
The default-constructed string member would simply be empty.
As explained before, the meaningfulness of a default constructed lambda
would be dependent on the meaningfulness of its default constructed
members. It is the responsability of the programmer to determine if the
default constructed lambda is meaningful or not. Note that it would be the
exact same logic as for the default construction of any user-defined type
and would thus be consistent with the rest of the language.

Also note that in the case of an arithmetic type:
int m{2};
auto add = [=](int n) {return m + n;};

I expect the generated lambda type to be roughly equivalent to :

struct lambda_type2 {
int m;
lambda_type2(int m) : m(m) {}
auto operator()(int n) const {return m + n;}
};
// ...
lambda_type2 add{2};

In this case:
lambda_type2 add; // not meaningful because int is not initialized (see
above for "partially formed state")
add = lambda_type2{2}; // Now ok (see the initial example for a motivation).

With also this case:
lambda_type2 add{}; // Ok: member initialized with empty braces, so value
is 0.

As noted Matt Calabrese, it's all about consistency. The current behaviour
of lambdas break the consistency with the equivalent user-defined types.
When introducing a new feature in the language, it should be primordial to
ensure that it works well with the other existing mechanisms of the
language, i.e. to ensure it doesn't break consistency.

One of the fundamental mechanisms of the language is the ability to build
new richer types based on existing types, by using existing types as
members. This means that the capabilities of the new type depend on the
capabilities of its members. For example, the new type is typically
comparable only if all its members are comparable. Same thing for default
construction. The consistency allows to reduce complexity (think of
std::vector being able to store std::vector, without having to handle any
special case because std::vector respects the same constrains that it put
on its stored type).

So, if I design a new type in generic code and any Callable (sub)members
happens to be a lambda, I have to drop default constructability. This seems
an artificial limitation, as the equivalent user-defined type to this
lambda type would be fine.
I'm talking here about default construction because it the topic of this
proposal, but I think the case is even stronger for equality.

Regards,

Jeremy
Post by Nicol Bolas
There is *no way* to distinguish between these two cases. As such,
whether it is valid to default construct a lambda that captures values is
not known and cannot be known. Therefore, permitting the default
construction of a capturing lambda creates fragile, brittle code.
That's bad.
This is like the rules of trivial copyability. It's possible to write
classes which violate the rules of trivial copyability yet still
technically could be. This is based on how the user-defined copy/move
operations work and what data is being stored. But despite this, we don't
allow trivially copying them to be well-defined C++ behavior. We only allow
it to be well-defined in circumstances where we can be *certain* that
such copying will be reasonable, where we can *statically* determine if
it will always be legitimate.
The same goes here. The only case where we can be certain that default
constructing a lambda will produce a valid lambda is if it is captureless.
--
*This email and any attachment thereto are confidential and intended solely
for the use of the individual or entity to whom they are addressed.If you
are not the intended recipient, please be advised that disclosing, copying,
distributing or taking any action in reliance on the contents of this email
is strictly prohibited. In such case, please immediately advise the sender,
and delete all copies and attachment from your system.This email shall not
be construed and is not tantamount to an offer, an acceptance of offer, or
an agreement by SoftBank Robotics Europe on any discussion or contractual
document whatsoever. No employee or agent is authorized to represent or
bind SoftBank Robotics Europe to third parties by email, or act on behalf
of SoftBank Robotics Europe by email, without express written confirmation
by SoftBank Robotics Europe’ duly authorized representatives.*
------------------------------




*Ce message électronique et éventuelles piÚces jointes sont confidentiels,
et exclusivement destinés à la personne ou l'entité à qui ils sont
adressés.Si vous n'êtes pas le destinataire visé, vous êtes prié de ne pas
divulguer, copier, distribuer ou prendre toute décision sur la foi de ce
message électronique. Merci d'en aviser immédiatement l'expéditeur et de
supprimer toutes les copies et éventuelles piÚces jointes de votre
systÚme.Ce message électronique n'équivaut pas à une offre, à une
acceptation d’offre, ou à un accord de SoftBank Robotics Europe sur toute
discussion ou document contractuel quel qu’il soit, et ne peut être
interprété comme tel. Aucun employé ou agent de SoftBank Robotics Europe
n'est autorisé à représenter ou à engager la société par email, ou à agir
au nom et pour le compte de la société par email, sans qu’une confirmation
écrite soit donnée par le représentant légal de SoftBank Robotics Europe ou
par toute autre personne ayant reçu délégation de pouvoir appropriée.*
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/dceec4a4-4067-4219-bb5b-b1f3aebdd41c%40isocpp.org.
Nicol Bolas
2016-05-26 14:42:15 UTC
Permalink
Post by j***@aldebaran.com
Post by Nicol Bolas
Post by j***@aldebaran.com
Post by Arthur O'Dwyer
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Viacheslav Usov
Again. I cannot see how that motivates a need for default
constructible lambdas.
It's never strictly a need, it's just a convenience.
[...]
FWIW, even though I lean towards more consistency with other class
types, I have only personally found default-construction for captureless
lambdas to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
This is a use-case I have also encountered in the wild. I would very
much like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.
Default-construction of *captureful* close types seems 100% dangerous
and bad.
int main() {
int y = 42;
auto x = [z=y](){ printf("%d\n", z); };
decltype(x) x2;
x2(); // does this print the "value" of a default-initialized
int? that's super dangerous!
}
We definitely shouldn't provide the user-programmer with any constructs
that allow him to shoot himself in the foot this badly, especially when
there's no motivating use-case for the feature.
See above for motivating examples.
Post by Arthur O'Dwyer
I would say that there's one minorly thought-provoking corner case in
int main() {
auto x = [z=42](){ printf("%d\n", z); };
decltype(x) x2; // ought this to compile?
x2(); // ought this to print 42?
}
My opinion is that "no, it certainly oughtn't to print 42", based on my
(correct?) belief that the meaning of [z=FOO] is not altered in any
way by whether or not FOO is a constant-expression. In my ideal world,
the above code would *refuse to compile*, because decltype(x) is quite
definitely a *captureful* closure type.
As explained above, the default constructor of the lambda should rely on
the default constructor of its members (same as a user-defined type). Thus,
the value of a default-constructed lambda would be meaningful depending of
the meaningfulness of the values of its default-constructed members.
I don't think you're getting the point.
If a lambda captures something, then the lambda function is either
written with the expectation that the capture will have the exact captured
value or it is not. If the function expects the value, then a default
constructed value of that type is *wrong* and will cause the function to
behave inappropriately.
I'm not sure to follow you.
std::string suffix{"abcd"};
auto append = [=](std::string const& str) {return str + suffix;};
struct lambda_type {
std::string suffix;
lambda_type(std::string s) : suffix(std::move(s)) {}
auto operator()(std::string const& str) const {return str + suffix;}
};
// ...
lambda_type append{"abcd"};
Why adding a default constructor to lambda_type would be wrong ?
You explained why this would be wrong with your `lambda_type` example.
Indeed, I have no idea why you posted it, since it undermines your entire
argument.

First, you should recall that `lambda_type` as you have declared it is *not
default constructible*. The rules for implicit default constructors is that
they exist unless you add a constructor. Since `lambda_type` has a
constructor, it won't get a default constructor unless you *explicitly* add
one.

So your "consistency" argument doesn't work. The standards committee
already ruled that such types are not implicitly default constructible. And
they did so for a very good reason. Therefore, making types that would
ordinarily be non-implicitly default constructible types into implicitly
default constructible types would in fact be *inconsistent* with existing
types.

Giving captureless lambdas a default constructor wouldn't be, since their
constructor's argument list is already de-facto empty.

Furthermore, the semantic purpose of `lambda_type` in your example is to
create an object which appends a string given at construction time to the
back of a string parameter. While it is syntactically valid to default
construct such an object, nobody would *want to*. Because if you did... *it
doesn't do anything*. Calling the default-constructed lambda is an
expensive no-op.

I would therefore assume most programmers would not declare a default
constructor for such a type, because the whole point of the type is that it
has a string. If the user wants to create one that appends the empty
string, then the user can pass an empty string to the non-default
constructor. That makes it abundantly clear what's going on.

And that's the best-case scenario for a default-constructed lambda: that it
does something pointless but harmless. Consider this:

auto GetTextureBinder(GLenum target, GLuint tex_id)
{
if(!IsValidTarget(target)) throw std::runtime_error("Not a valid target."
);
if(!glIsTexture(tex_id)) throw std::runtime_error("Not a valid texture");

return [target, tex_id]() {glBindTexture(target, tex_id);}
}

This function validates a parameter, then returns a function that uses that
parameter. By the rules of C++ as they stand now, I have reasonable
assurance that the returned functor will always have a valid target. There
is nothing you can do to break this lambda externally without employing
perfidy (or `glDeleteTexture(tex_id)`). And that in fact is the *the goal*
of this function: to produce a lambda that binds the texture successfully,
without having to match `target` with the texture it goes to..

Default constructing this type will produce a *semantically meaningless*
object. To call that lambda will *at best*produce an OpenGL error; If it is
default initialized, then you may get total garbage. And remember: the
whole point of the lambda is to *avoid* such errors, to bind a valid
texture and its appropriate target together.

So in your world, how do I get my reasonable assurance back? How do I *turn
off* default construction of a lambda when that would lead to a
non-functioning lambda? Should I put redundant checking in my lambda to
make sure that values which were verified previously remain valid, even
though the whole point was to ensure that it was valid *before* creating
the lambda? Or do I have to abandon lambdas entirely and go back to a
user-built `struct`?

The default-constructed string member would simply be empty.
Post by j***@aldebaran.com
As explained before, the meaningfulness of a default constructed lambda
would be dependent on the meaningfulness of its default constructed members.
No, it depends on the meaningfulness of the lambda function when used with
default constructed members.
Post by j***@aldebaran.com
It is the responsability of the programmer to determine if the default
constructed lambda is meaningful or not.
Not under your proposal.

Responsibility has two parts: what you're have to do and the *ability* to
do it. If it is impossible for you to do something, then you're not
responsible for failing to do it.

Under your proposal, it is *impossible* for a programmer to turn off
default construction of a lambda when it is not meaningful. If the types
permit default construction, there is nothing the lambda writer can do
about it.

And no, using special non-default-constructible wrappers in your captures
is not a valid alternative.
Post by j***@aldebaran.com
As noted Matt Calabrese, it's all about consistency. The current behaviour
of lambdas break the consistency with the equivalent user-defined types.
When introducing a new feature in the language, it should be primordial to
ensure that it works well with the other existing mechanisms of the
language, i.e. to ensure it doesn't break consistency.
OK, the "consistency" ship sailed on lambdas when they decided that you had
to stick `mutable` at the end in order to modify the captured values.
Expecting consistency from lambdas is foolish at this point.

Also, as previously stated, "consistency" makes no sense here, because the
types in a struct do not alone determine whether it is default
constructible. So what are you trying to be "consistent" with here?

Lastly, consistency for its own sake is silly. Especially when that
"consistency" creates the possibility of bugs that didn't exist before.

One of the fundamental mechanisms of the language is the ability to build
Post by j***@aldebaran.com
new richer types based on existing types, by using existing types as
members. This means that the capabilities of the new type depend on the
capabilities of its members. For example, the new type is typically
comparable only if all its members are comparable. Same thing for default
construction.
I can *turn off* default construction. I can also turn on default
construction even when types are not default constructible.

Your suggestion doesn't give me that power.
Post by j***@aldebaran.com
The consistency allows to reduce complexity (think of std::vector being
able to store std::vector, without having to handle any special case
because std::vector respects the same constrains that it put on its stored
type).
Precisely what complexity is being reduced here?

Show me a template function which takes an arbitrary callable as a
parameter, that would *reasonably decide* to call a default-constructed
version of that callable.

Nobody would write such a thing. Why? Because you'd break with this:

int MyFunc();

CallTemplate(&MyFunc);

A default constructed function pointer is always a bad thing. Also:

std::function<int()> MyFunc = ...;

CallTemplate(MyFunc);

A default-constructed `function` is always a bad thing.

When it comes to non-functioning code, I'd personally rather get a compile
error instead of a runtime crash.

The *only reason* I can see why you would ever want to default construct
one is as a temporary measure, a stopgap on the way to performing a
copy/move assignment into that value. And those cases are exceedingly rare,
and can be dealt with by using `aligned_storage` and placement new.

So, if I design a new type in generic code and any Callable (sub)members
Post by j***@aldebaran.com
happens to be a lambda, I have to drop default constructability. This seems
an artificial limitation, as the equivalent user-defined type to this
lambda type would be fine.
As previously explained, the "equivalent user-defined type" would not be
default constructible. Also as previously explained, the ability to default
construct a lambda does not ensure that calling the function will be valid.
So if you were permitted to default construct them, you would not be able
to tell whether it was valid to do so.

Therefore, your template code should not attempt to default construct them.
So you've gained *nothing*.

The only lambdas that are *guaranteed* to be legitimate candidates for
default construction are captureless ones.
Post by j***@aldebaran.com
I'm talking here about default construction because it the topic of this
proposal, but I think the case is even stronger for equality.
Equality is irrelevant, since we'll get that with the implicit comparison
operators proposal.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/871cb4cd-3df5-4421-a318-f69bb90b3ca2%40isocpp.org.
Nevin Liber
2016-05-26 14:56:37 UTC
Permalink
Post by j***@aldebaran.com
std::string suffix{"abcd"};
auto append = [=](std::string const& str) {return str + suffix;};
struct lambda_type {
std::string suffix;
lambda_type(std::string s) : suffix(std::move(s)) {}
auto operator()(std::string const& str) const {return str + suffix;}
};
// ...
lambda_type append{"abcd"};
Why wouldn't I just create a different lambda if I wanted to append to
nothing as opposed to appending to a captured string?

Again, I'm not seeing why having a lambda with only two possible states is
all that useful, especially in generic code.

Yes, I know you can abuse a mutable function call operator to add more
states, but that is an awfully convoluted way to get around writing a class.
Post by j***@aldebaran.com
As noted Matt Calabrese, it's all about consistency. The current behaviour
of lambdas break the consistency with the equivalent user-defined types.
Huh? UDTs are not required to be default constructible. UDTs typically
can have far more than two states.
Post by j***@aldebaran.com
One of the fundamental mechanisms of the language is the ability to build
new richer types based on existing types, by using existing types as
members.
Which is not the intention of lambdas. How do you envision using a
capturing lambda (default constructed or not) as an aggregate member of
another type? Please post a code example of what you want to work in this
case.
--
Nevin ":-)" Liber <mailto:***@eviloverlord.com> +1-847-691-1404
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGg_6%2BNxrUkWT8s3RxCG0xbsDO_tYiQPx%3D3M9%2B2-rGZrUMTOoQ%40mail.gmail.com.
j***@aldebaran.com
2016-05-27 08:58:26 UTC
Permalink
Post by Nevin Liber
Post by j***@aldebaran.com
std::string suffix{"abcd"};
auto append = [=](std::string const& str) {return str + suffix;};
struct lambda_type {
std::string suffix;
lambda_type(std::string s) : suffix(std::move(s)) {}
auto operator()(std::string const& str) const {return str + suffix;}
};
// ...
lambda_type append{"abcd"};
Why wouldn't I just create a different lambda if I wanted to append to
nothing as opposed to appending to a captured string?
Again, I'm not seeing why having a lambda with only two possible states is
all that useful, especially in generic code.
See the example below.
Post by Nevin Liber
Yes, I know you can abuse a mutable function call operator to add more
states, but that is an awfully convoluted way to get around writing a class.
Post by j***@aldebaran.com
As noted Matt Calabrese, it's all about consistency. The current
behaviour of lambdas break the consistency with the equivalent user-defined
types.
Huh? UDTs are not required to be default constructible. UDTs typically
can have far more than two states.
Post by j***@aldebaran.com
One of the fundamental mechanisms of the language is the ability to build
new richer types based on existing types, by using existing types as
members.
Which is not the intention of lambdas. How do you envision using a
capturing lambda (default constructed or not) as an aggregate member of
another type? Please post a code example of what you want to work in this
case.
Here is an more complex example that should compile. I hope this will
answer your questions.
It's useful to default construct I in find_last.

#include <iterator>
#include <utility>
#include <type_traits>
#include <forward_list>
#include <string>
#include <iostream>

// Useful in this example.
template<typename T>
bool operator!=(T const& a, T const& b) {
return !(a == b);
}

// Find the iterator right before end, that is the last iterator pointing to
// a valid element of the half-open bounded range.
template<typename I> // ForwardIterator I
I find_last(I b, I e) {
// Precondition: bounded_range(b, e) && b != e
I i; // It's possible to do this only if all I's members are default
constructible, which is not the case if there is a lambda.
// Note that it doesn't matter here if the default-constructed value is
meaningful or not.
do {
i = b;
++b;
} while (b != e);
return i;
}

// In this case, it is useful to default contruct i, because we don't have
the value yet, and the scope of i exceed the loop.

// A Transformation is a Regular Callable with the signature T (T).
// We could also use an Action (semantically equivalent to a Transformation,
// but with the signature void (T&)) if the assignment of I is expensive.
template<typename I, typename F> // ForwardIterator I, Transformation<I> F
struct iter_transfo_t {
I i;
F f; // This is a Callable, and it would be nice if it could be a
lambda.
// ...
iter_transfo_t& operator++() {
i = f(i);
return *this;
}
friend bool operator==(iter_transfo_t const& a, iter_transfo_t const&
b) {
// For now, doesn't compile if f is a lambda, but let's assume it's
ok.
return a.i == b.i && a.f == b.f;
}
// As a side note, would be also nice to have operator<(), for example
to be able to use this type
// as the key type of a map.
// The definition would be (a.i < b.i) || (!(b.i < a.i) && a.f < b.f),
// assuming that < on I follows the trichotomy law (meaning
// !(i < j) && !(j < i) implies i == j).
// ...
};

template<typename I> // ForwardIterator I
auto strided_iter(I&& i, size_t step) {
auto f = [=](I i) {std::advance(i, step); return i;};
return iter_transfo_t<std::decay_t<I>, decltype(f)>{std::forward<I>(i),
f};
}

// Let's imagine the elements follow the pattern
// firstname0, lastname0, firstname1, lastname1, ...
// We could use this to find the last firstname.
template<typename I> // ForwardIterator I
I find_last_even_positionned(I b, I e) {
// Precondition: bounded_range(b, e) && std::distance(b, e) >= 2 &&
std::distance(b, e) % 2 == 0
return find_last(strided_iter(b, 2u), strided_iter(e, 2u)).i;
}

// We could implement other algorithms similar to find_last_name, by using
other transformations,
// (typically implemented by lambdas) for example to find the last prime
number of a range, and so on.
// The transformations allow to implement any traversal of the range.

int main() {
using namespace std;
// The only purpose of using a forward_list is to show that the
algorithm works with forward iterators.
forward_list<string> names{"John", "Rambo", "Marty", "McFly",
"Indiana", "Jones"};
auto it_first_name = find_last_even_positionned(begin(names),
end(names));
cout << *it_first_name << '\n'; // output Indiana
return 0;
}
Post by Nevin Liber
--
+1-847-691-1404
If we replace the lambda in strided_iter by the following type, everything
is ok :

struct advance_t {
size_t step;
template<typename I>
I operator()(I i) const {
std::advance(i, step);
return i;
}
friend bool operator==(advance_t const &a, advance_t const& b) {
return a.step == b.step;
}
// Not mandatory in the above example, but nice to have:
friend bool operator<(advance_t const &a, advance_t const& b) {
return a.step < b.step;
}
};

template<typename I> // ForwardIterator I
auto strided_iter(I&& i, size_t step) {
return iter_transfo_t<std::decay_t<I>, advance_t>{std::forward<I>(i),
{step}};
}


Regards,

Jeremy
--
*This email and any attachment thereto are confidential and intended solely
for the use of the individual or entity to whom they are addressed.If you
are not the intended recipient, please be advised that disclosing, copying,
distributing or taking any action in reliance on the contents of this email
is strictly prohibited. In such case, please immediately advise the sender,
and delete all copies and attachment from your system.This email shall not
be construed and is not tantamount to an offer, an acceptance of offer, or
an agreement by SoftBank Robotics Europe on any discussion or contractual
document whatsoever. No employee or agent is authorized to represent or
bind SoftBank Robotics Europe to third parties by email, or act on behalf
of SoftBank Robotics Europe by email, without express written confirmation
by SoftBank Robotics Europe’ duly authorized representatives.*
------------------------------




*Ce message électronique et éventuelles piÚces jointes sont confidentiels,
et exclusivement destinés à la personne ou l'entité à qui ils sont
adressés.Si vous n'êtes pas le destinataire visé, vous êtes prié de ne pas
divulguer, copier, distribuer ou prendre toute décision sur la foi de ce
message électronique. Merci d'en aviser immédiatement l'expéditeur et de
supprimer toutes les copies et éventuelles piÚces jointes de votre
systÚme.Ce message électronique n'équivaut pas à une offre, à une
acceptation d’offre, ou à un accord de SoftBank Robotics Europe sur toute
discussion ou document contractuel quel qu’il soit, et ne peut être
interprété comme tel. Aucun employé ou agent de SoftBank Robotics Europe
n'est autorisé à représenter ou à engager la société par email, ou à agir
au nom et pour le compte de la société par email, sans qu’une confirmation
écrite soit donnée par le représentant légal de SoftBank Robotics Europe ou
par toute autre personne ayant reçu délégation de pouvoir appropriée.*
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/a06ba085-0637-46d9-8249-af668640c8b1%40isocpp.org.
Nicol Bolas
2016-05-27 15:29:36 UTC
Permalink
Post by j***@aldebaran.com
Post by Nevin Liber
Post by j***@aldebaran.com
std::string suffix{"abcd"};
auto append = [=](std::string const& str) {return str + suffix;};
struct lambda_type {
std::string suffix;
lambda_type(std::string s) : suffix(std::move(s)) {}
auto operator()(std::string const& str) const {return str + suffix;}
};
// ...
lambda_type append{"abcd"};
Why wouldn't I just create a different lambda if I wanted to append to
nothing as opposed to appending to a captured string?
Again, I'm not seeing why having a lambda with only two possible states
is all that useful, especially in generic code.
See the example below.
Post by Nevin Liber
Yes, I know you can abuse a mutable function call operator to add more
states, but that is an awfully convoluted way to get around writing a class.
Post by j***@aldebaran.com
As noted Matt Calabrese, it's all about consistency. The current
behaviour of lambdas break the consistency with the equivalent user-defined
types.
Huh? UDTs are not required to be default constructible. UDTs typically
can have far more than two states.
Post by j***@aldebaran.com
One of the fundamental mechanisms of the language is the ability to
build new richer types based on existing types, by using existing types as
members.
Which is not the intention of lambdas. How do you envision using a
capturing lambda (default constructed or not) as an aggregate member of
another type? Please post a code example of what you want to work in this
case.
Here is an more complex example that should compile. I hope this will
answer your questions.
It's useful to default construct I in find_last.
So you have a type `iter_transfo_t` which needs to be default
constructible. But that type needs to store something which is decidedly
not default constructible. The only saving grace of this concept is that,
while default constructing ForwardIterators is legal, it's not legal to
actually do anything with them without copying into them. So just work
around the limitation. If `F` is default constructible, then use it. If `F`
is not, then don't store `F` directly. Use a `std::function` or a
lighter-weight wrapper which is default constructible. Here's one example
of such a wrapper:

template<typename T>
class DefaultConstructible
{
public:
DefaultConstructible() = default;

DefaultConstructible(const DefaultConstructible &other)
: t(new(&t_data) T(other) {}
DefaultConstructible(DefaultConstructible &&other)
: t(new(&t_data) T(std::move(other)) {}
const DefaultConstructible &operator=(const DefaultConstructible &other)
{
if(t)
{
if(other.t)
*t = *other.t;
else
{
t->~T();
t = nullptr;
}
}
else
{
if(other.t)
t = new(&t_data) T(*other.t);
}
return *this;
}
const DefaultConstructible &operator=(DefaultConstructible &&other)
{
if(t)
{
if(other.t)
*t = std::move(*other.t);
else
{
t->~T();
t = nullptr;
}
}
else
{
if(other.t)
t = new(&t_data) T(std::move(*other.t));
}
return *this;
}

operator T*() {return t;}
operator const T*() const {return t;}

T& operator*() {return *t;}
const T& operator*() {return *t;}

//SFINAE protect if DefaultCosntructible is not equality comparable.
bool operator==(const DefaultConstructible &rhs) const
{
if(t && rhs.t)
return *t == *rhs.t;

return false; //If one of them doesn't have a proper value yet, you
can't compare them reasonably.
}

//Add other comparisons as needed.

~DefaultConstructible() {if(t) t->~T();}

private:
std::aligned_storage<sizeof(T), alignof(T)>::type t_data;
T *t = nullptr;
};
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/56e840d4-2b87-41d3-81a4-4b9cca700c97%40isocpp.org.
Thiago Macieira
2016-05-26 16:35:11 UTC
Permalink
Post by j***@aldebaran.com
int m{2};
auto add = [=](int n) {return m + n;};
struct lambda_type2 {
int m;
lambda_type2(int m) : m(m) {}
auto operator()(int n) const {return m + n;}
};
// ...
lambda_type2 add{2};
In this case and in many others, I'd expect the compiler to do constant
propagation and remove the member "m" completely. That would make the lambda:

struct lambda_type2 {
auto operator()(int n) const {return 2 + n;}
};
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/56320921.nVkzP2oIad%40tjmaciei-mobl1.
Arthur O'Dwyer
2016-05-26 18:35:39 UTC
Permalink
Em quinta-feira, 26 de maio de 2016, às 03:14:55 PDT,
Post by j***@aldebaran.com
int m{2};
auto add = [=](int n) {return m + n;};
struct lambda_type2 {
int m;
lambda_type2(int m) : m(m) {}
auto operator()(int n) const {return m + n;}
};
// ...
lambda_type2 add{2};
In this case and in many others, I'd expect the compiler to do constant
struct lambda_type2 {
auto operator()(int n) const {return 2 + n;}
};
Thiago, I think from a machine-level point of view you're certainly
*correct* — but if you believe your comment to be *relevant* at the
C++-language-semantics level, could you please clarify how?

Part of the point of my earlier post was to demonstrate that even these two
lambdas

auto captureless = [](int n) { return 2 + n; };
auto captureful = [m=2](int n) { return m + n; };

have fundamentally different behaviors in C++. captureless is implicitly
convertible to int(*)(int), whereas captureful is not implicitly
convertible to int(*)(int), and by the current rules of the C++ language
the compiler is *not allowed* to make it implicitly convertible to
int(*)(int).

–Arthur
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CADvuK0K5s0FX0_L5MbU39mzynQW-3KKgXwa4V8%2BJD94ZLXm0dQ%40mail.gmail.com.
Nicol Bolas
2016-05-27 00:26:44 UTC
Permalink
Post by Arthur O'Dwyer
Em quinta-feira, 26 de maio de 2016, às 03:14:55 PDT,
Post by j***@aldebaran.com
int m{2};
auto add = [=](int n) {return m + n;};
struct lambda_type2 {
int m;
lambda_type2(int m) : m(m) {}
auto operator()(int n) const {return m + n;}
};
// ...
lambda_type2 add{2};
In this case and in many others, I'd expect the compiler to do constant
struct lambda_type2 {
auto operator()(int n) const {return 2 + n;}
};
Thiago, I think from a machine-level point of view you're certainly
*correct* — but if you believe your comment to be *relevant* at the
C++-language-semantics level, could you please clarify how?
He's not trying to say that, at the language level, one should be
equivalent to the other. He's saying that the compiler has the choice right
now to optimize the capturing lambda "as if" it were a non-capturing one.
You don't get the language distinction, but you do get the *performance*.

With the proposed change, forcing capturing lambdas to be default
constructible, that optimization would now become *impossible*.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/230b692a-7b91-48f2-95a9-7559ad8f22bb%40isocpp.org.
Arthur O'Dwyer
2016-05-27 00:29:23 UTC
Permalink
Post by Nicol Bolas
Post by Arthur O'Dwyer
Em quinta-feira, 26 de maio de 2016, às 03:14:55 PDT,
Post by j***@aldebaran.com
int m{2};
auto add = [=](int n) {return m + n;};
struct lambda_type2 {
int m;
lambda_type2(int m) : m(m) {}
auto operator()(int n) const {return m + n;}
};
// ...
lambda_type2 add{2};
In this case and in many others, I'd expect the compiler to do constant
struct lambda_type2 {
auto operator()(int n) const {return 2 + n;}
};
Thiago, I think from a machine-level point of view you're certainly
*correct* — but if you believe your comment to be *relevant* at the
C++-language-semantics level, could you please clarify how?
He's not trying to say that, at the language level, one should be
equivalent to the other. He's saying that the compiler has the choice right
now to optimize the capturing lambda "as if" it were a non-capturing one.
You don't get the language distinction, but you do get the *performance*.
With the proposed change, forcing capturing lambdas to be default
constructible, that optimization would now become *impossible*.
Ah, I see. That makes sense.

–Arthur
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CADvuK0JSQMO6ms33G8H4udQJ--ZrVkc8-19j7yzbSPeA6BjLBA%40mail.gmail.com.
FrankHB1989
2016-05-27 12:10:15 UTC
Permalink
Post by j***@aldebaran.com
Post by Nicol Bolas
Post by j***@aldebaran.com
Post by Arthur O'Dwyer
Post by 'Matt Calabrese' via ISO C++ Standard - Future Proposals
Post by Viacheslav Usov
Again. I cannot see how that motivates a need for default
constructible lambdas.
It's never strictly a need, it's just a convenience.
[...]
FWIW, even though I lean towards more consistency with other class
types, I have only personally found default-construction for captureless
lambdas to be desirable in my particular use-cases, and that's all that I
// Use a lambda as a stateless deleter function
std::unique_ptr<some_type, decltype([](some_type* ptr) {
some_deletion_function(ptr); })> object(create_some_type());
This is a use-case I have also encountered in the wild. I would very
much like this (lambdas in unevaluated contexts, default-construction of
captureless closure types) to Just Work.
Default-construction of *captureful* close types seems 100% dangerous
and bad.
int main() {
int y = 42;
auto x = [z=y](){ printf("%d\n", z); };
decltype(x) x2;
x2(); // does this print the "value" of a default-initialized
int? that's super dangerous!
}
We definitely shouldn't provide the user-programmer with any constructs
that allow him to shoot himself in the foot this badly, especially when
there's no motivating use-case for the feature.
See above for motivating examples.
Post by Arthur O'Dwyer
I would say that there's one minorly thought-provoking corner case in
int main() {
auto x = [z=42](){ printf("%d\n", z); };
decltype(x) x2; // ought this to compile?
x2(); // ought this to print 42?
}
My opinion is that "no, it certainly oughtn't to print 42", based on my
(correct?) belief that the meaning of [z=FOO] is not altered in any
way by whether or not FOO is a constant-expression. In my ideal world,
the above code would *refuse to compile*, because decltype(x) is quite
definitely a *captureful* closure type.
As explained above, the default constructor of the lambda should rely on
the default constructor of its members (same as a user-defined type). Thus,
the value of a default-constructed lambda would be meaningful depending of
the meaningfulness of the values of its default-constructed members.
I don't think you're getting the point.
If a lambda captures something, then the lambda function is either
written with the expectation that the capture will have the exact captured
value or it is not. If the function expects the value, then a default
constructed value of that type is *wrong* and will cause the function to
behave inappropriately.
I'm not sure to follow you.
std::string suffix{"abcd"};
auto append = [=](std::string const& str) {return str + suffix;};
struct lambda_type {
std::string suffix;
lambda_type(std::string s) : suffix(std::move(s)) {}
auto operator()(std::string const& str) const {return str + suffix;}
};
// ...
lambda_type append{"abcd"};
Why adding a default constructor to lambda_type would be wrong ?
The default-constructed string member would simply be empty.
As explained before, the meaningfulness of a default constructed lambda
would be dependent on the meaningfulness of its default constructed
members. It is the responsability of the programmer to determine if the
default constructed lambda is meaningful or not. Note that it would be the
exact same logic as for the default construction of any user-defined type
and would thus be consistent with the rest of the language.
int m{2};
auto add = [=](int n) {return m + n;};
struct lambda_type2 {
int m;
lambda_type2(int m) : m(m) {}
auto operator()(int n) const {return m + n;}
};
// ...
lambda_type2 add{2};
lambda_type2 add; // not meaningful because int is not initialized (see
above for "partially formed state")
add = lambda_type2{2}; // Now ok (see the initial example for a motivation).
lambda_type2 add{}; // Ok: member initialized with empty braces, so value
is 0.
As noted Matt Calabrese, it's all about consistency. The current behaviour
of lambdas break the consistency with the equivalent user-defined types.
When introducing a new feature in the language, it should be primordial to
ensure that it works well with the other existing mechanisms of the
language, i.e. to ensure it doesn't break consistency.
It sounds absurd to require arbitrary data types having capability of
holding not-constructed/indeterminate state. For integer types that may be
reasonable because the allowed representations are required to be uniformly
mapped to the underlying storage in some predicable manners.
Note that allowing indeterminate value stored in object of such types is
already not consistent, e.g. reading uninitialized unsigned char vs. int.
Post by j***@aldebaran.com
One of the fundamental mechanisms of the language is the ability to build
new richer types based on existing types, by using existing types as
members. This means that the capabilities of the new type depend on the
capabilities of its members. For example, the new type is typically
comparable only if all its members are comparable. Same thing for default
construction. The consistency allows to reduce complexity (think of
std::vector being able to store std::vector, without having to handle any
special case because std::vector respects the same constrains that it put
on its stored type).
Strictly speaking, these existing types are ill-designed. Unless I'm
working on some particular forms of interop, I want optional<T> but not T*;
similarly, I better have some indeterminate<T> rather than T with
indeterminate state specified in subtle additional rules of the language. I
do not like to pay for what I deliberately don't want to have, because it
will distorts my intent and causes unnecessary mental cost.
Post by j***@aldebaran.com
So, if I design a new type in generic code and any Callable (sub)members
happens to be a lambda, I have to drop default constructability. This seems
an artificial limitation, as the equivalent user-defined type to this
lambda type would be fine.
I'm talking here about default construction because it the topic of this
proposal, but I think the case is even stronger for equality.
Are you serious? Equality is not strictly necessary in every theories, but
any formal systems (e.g. set theories) being practical and general enough
will provide some sorts of it, otherwise you can't determine the identity
of things being described. (And for theories do not provide one, they will
rely on the base theories which provide it.) You may not use it explicitly
because the underlying system has implied it, for example, the C++ type
system (which is nominal) provides type equivalence (btw, sadly
std::is_same cannot be overloaded) being widely used in many contexts of
the language. To implement such a system using meta language (here it is
C++), an equality function is always needed, so it is better provided in a
standard form, to make the use consistent. On the other hand, the property
of "being constructible without parameters" or "having exact one
standardized undetermined state" is definitely not so general in both meta
languages and object languages. It is occasionally useful in C++ as an
object language because the default constructor is specified being one of
"special member functions", despite there is actually no need to make it
special like a copy constructor or a destructor because the
default-initialization is concerned with less contexts in the language
(mostly required due to being compatible with C).

Regards,
Post by j***@aldebaran.com
Jeremy
Post by Nicol Bolas
There is *no way* to distinguish between these two cases. As such,
whether it is valid to default construct a lambda that captures values is
not known and cannot be known. Therefore, permitting the default
construction of a capturing lambda creates fragile, brittle code.
That's bad.
This is like the rules of trivial copyability. It's possible to write
classes which violate the rules of trivial copyability yet still
technically could be. This is based on how the user-defined copy/move
operations work and what data is being stored. But despite this, we don't
allow trivially copying them to be well-defined C++ behavior. We only allow
it to be well-defined in circumstances where we can be *certain* that
such copying will be reasonable, where we can *statically* determine if
it will always be legitimate.
The same goes here. The only case where we can be certain that default
constructing a lambda will produce a valid lambda is if it is captureless.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/17e4246d-789e-4795-89be-48e6fb1598b4%40isocpp.org.
'Johannes Schaub' via ISO C++ Standard - Future Proposals
2016-05-24 17:32:45 UTC
Permalink
Currently a lambda may rely on its captured by copy values to have not
changed unless it itself changed them.

If you allow default construction, this invariant will break. Maybe bad.
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).
Post by Klaim - Joël Lamotte
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
Otherwise I would like to propose to add
ClosureType() = default;
to the definition of the closure generated by a lambda, if it's not
already proposed somewhere.
Post by Klaim - Joël Lamotte
This would help working with closures in generic context where we assume
that the callable type is regular (it's not enough but it helps);
template< class T, class Callable >
struct FatForwardIterator
{
T value;
Callable incr; // deactivate the default constructor if it's a closure
//... etc
FatForwardIterator& operator++()
{
// ...
value = incr(value);
return *this;
}
};
// let's assume that the proposal to generate default comparisons is on...
template< class T, class Callable >
auto make_fat_iterator( T initial_value, Callable incr )
{
return FatForwardIterator<T, Callable>{ initial_value, incr };
}
template< class IteratorType>
void some_algorithm( IteratorType begin, IteratorType end )
{
IteratorType previous; // error: IteratorType do not have a default
constructor.
Post by Klaim - Joël Lamotte
do
{
previous = begin;
++begin;
// ... use previous
}
while( begin != end );
}
void foo()
{
auto times2 = [](auto value){ return value * 2; };
auto begin = make_fat_iterator( 1, times2 );
auto end = make_fat_iterator( 8, times2 );
// ...
some_algorithm( begin, end ); // fails to compile
}
--
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Future Proposals" group.
Post by Klaim - Joël Lamotte
To unsubscribe from this group and stop receiving emails from it, send an
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91ONZtVMdUZQ_JpyVwcV0%2BsXVdUUcSnmgrzr4u6viuVk5WQ%40mail.gmail.com
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CANu6V4VEBfkXnmPYGn6gW%3DKgSqB3NPFNuWJ%2BMtDgUM%2Bfp8ZWqw%40mail.gmail.com.
'Johannes Schaub' via ISO C++ Standard - Future Proposals
2016-05-24 17:36:57 UTC
Permalink
This also means that even though the corresponding member of the lambda is
nonconst, a compiler can constant propagate copy captured members if the
lambda body hasn't changed the closure copy data member.

Allowing default constructing members destroys this nice optimization
opportunity.

Am 24.05.2016 19:32 schrieb "Johannes Schaub" <
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Currently a lambda may rely on its captured by copy values to have not
changed unless it itself changed them.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
If you allow default construction, this invariant will break. Maybe bad.
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by Klaim - Joël Lamotte
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
Otherwise I would like to propose to add
ClosureType() = default;
to the definition of the closure generated by a lambda, if it's not
already proposed somewhere.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by Klaim - Joël Lamotte
This would help working with closures in generic context where we assume
that the callable type is regular (it's not enough but it helps);
template< class T, class Callable >
struct FatForwardIterator
{
T value;
Callable incr; // deactivate the default constructor if it's a closure
//... etc
FatForwardIterator& operator++()
{
// ...
value = incr(value);
return *this;
}
};
// let's assume that the proposal to generate default comparisons is on...
template< class T, class Callable >
auto make_fat_iterator( T initial_value, Callable incr )
{
return FatForwardIterator<T, Callable>{ initial_value, incr };
}
template< class IteratorType>
void some_algorithm( IteratorType begin, IteratorType end )
{
IteratorType previous; // error: IteratorType do not have a default
constructor.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by Klaim - Joël Lamotte
do
{
previous = begin;
++begin;
// ... use previous
}
while( begin != end );
}
void foo()
{
auto times2 = [](auto value){ return value * 2; };
auto begin = make_fat_iterator( 1, times2 );
auto end = make_fat_iterator( 8, times2 );
// ...
some_algorithm( begin, end ); // fails to compile
}
--
You received this message because you are subscribed to the Google
Groups "ISO C++ Standard - Future Proposals" group.
Post by 'Johannes Schaub' via ISO C++ Standard - Future Proposals
Post by Klaim - Joël Lamotte
To unsubscribe from this group and stop receiving emails from it, send
To view this discussion on the web visit
https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAOU91ONZtVMdUZQ_JpyVwcV0%2BsXVdUUcSnmgrzr4u6viuVk5WQ%40mail.gmail.com
.
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CANu6V4UJ2Z7-fvrw8YhYmwcvSH_RTeBMWq%3D4xVcBxgY3VGo9aw%40mail.gmail.com.
FrankHB1989
2016-05-27 11:35:47 UTC
Permalink
圚 2016幎5月24日星期二 UTC+8䞋午10:25:40Klaim - Joël Lamotte写道
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
Otherwise I would like to propose to add
ClosureType() = default;
to the definition of the closure generated by a lambda, if it's not
already proposed somewhere.
This would help working with closures in generic context where we assume
that the callable type is regular (it's not enough but it helps);
template< class T, class Callable >
struct FatForwardIterator
{
T value;
Callable incr; // deactivate the default constructor if it's a closure
//... etc
FatForwardIterator& operator++()
{
// ...
value = incr(value);
return *this;
}
};
// let's assume that the proposal to generate default comparisons is on...
template< class T, class Callable >
auto make_fat_iterator( T initial_value, Callable incr )
{
return FatForwardIterator<T, Callable>{ initial_value, incr };
}
template< class IteratorType>
void some_algorithm( IteratorType begin, IteratorType end )
{
IteratorType previous; // error: IteratorType do not have a default
constructor.
do
{
previous = begin;
++begin;
// ... use previous
}
while( begin != end );
}
void foo()
{
auto times2 = [](auto value){ return value * 2; };
auto begin = make_fat_iterator( 1, times2 );
auto end = make_fat_iterator( 8, times2 );
// ...
some_algorithm( begin, end ); // fails to compile
}
Why should an iterator be DefaultConstructible?
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/bf3a6595-4e16-442e-a166-49faaef5c5d1%40isocpp.org.
Daniel Krügler
2016-05-27 11:41:17 UTC
Permalink
在 2016年5月24日星期二 UTC+8下午10:25:40,Klaim - Joël Lamotte写道:
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
Otherwise I would like to propose to add
ClosureType() = default;
to the definition of the closure generated by a lambda, if it's not
already proposed somewhere.
This would help working with closures in generic context where we assume
that the callable type is regular (it's not enough but it helps);
template< class T, class Callable >
struct FatForwardIterator
{
T value;
Callable incr; // deactivate the default constructor if it's a closure
//... etc
FatForwardIterator& operator++()
{
// ...
value = incr(value);
return *this;
}
};
// let's assume that the proposal to generate default comparisons is on...
[..]
Why should an iterator be DefaultConstructible?
There is no DefaultConstructible requirement for an InputIterator or
an OutputIterator, but there is one from ForwardIterator on (indicated
by the naming of the OPs types there seems to be a forward iterator
involved).

- Daniel
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/CAGNvRgDVgNsm4RkDUZvH87fnV-XA727zKpNJqmqK4KVv8AHr6Q%40mail.gmail.com.
FrankHB1989
2016-05-27 12:44:24 UTC
Permalink
圚 2016幎5月27日星期五 UTC+8䞋午7:41:19Daniel KrÃŒgler写道
Post by Klaim - Joël Lamotte
Post by FrankHB1989
圚 2016幎5月24日星期二 UTC+8䞋午10:25:40Klaim - Joël Lamotte写道
Post by Klaim - Joël Lamotte
I am looking for the reason why lambda expressions are explicitly
not defining a default constructor (at least depending on the arguments
constructors).
I cannot find a specific reason for that so far.
Does anyone here have a source about this point?
Otherwise I would like to propose to add
ClosureType() = default;
to the definition of the closure generated by a lambda, if it's not
already proposed somewhere.
This would help working with closures in generic context where we
assume
Post by FrankHB1989
Post by Klaim - Joël Lamotte
that the callable type is regular (it's not enough but it helps);
template< class T, class Callable >
struct FatForwardIterator
{
T value;
Callable incr; // deactivate the default constructor if it's a
closure
Post by FrankHB1989
Post by Klaim - Joël Lamotte
//... etc
FatForwardIterator& operator++()
{
// ...
value = incr(value);
return *this;
}
};
// let's assume that the proposal to generate default comparisons is
on...
[..]
Post by FrankHB1989
Why should an iterator be DefaultConstructible?
There is no DefaultConstructible requirement for an InputIterator or
an OutputIterator, but there is one from ForwardIterator on (indicated
by the naming of the OPs types there seems to be a forward iterator
involved).
Yes, I should mean that specifically for forward iterators. However, for
iterators mostly used (e.g. as member type of containers), they have to
meet the requirement of being DefaultConstructible. Note forward iterators
are required to be value-initialized to be a general pass-the-end indicator
of an empty sequence which I find no algorithms relies on; I think it
superfluous.
Post by Klaim - Joël Lamotte
- Daniel
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/ea49fc91-9018-4a83-9541-4739deab8774%40isocpp.org.
Loading...