Discussion:
Solving the std::swap embarrassment*?
(too old to reply)
Marc Mutz
2017-05-02 18:23:38 UTC
Permalink
* technical term, not judgmental

Hi,

I thought this was solved in C++11 but Ville proved me wrong today:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#229 motivates
partial function specialisation for solving the problem that the world outside
namespace std has to write

using std::swap;
swap(a, b);

(where swap() is a stand-in for any std function, incl. e.g. sin(),
for_each()). They can't just write

std::swap(a, b);

because class authors are forbidden by [namespace.std] to add new declarations
(that includes function overloads) to namespace std, even though they are
allowed to partically specialise class templates there. You cannot partially
specialise function templates, you need to overload them. But you can't. Not
in namespace std. So users need to activate ADL, while still allowing falling
back to the std version, thus the using declaration + unqualified name lookup.

I don't know when I learned about this the first time, but I do know from
where: Scott Meyer's books. Those are ~20 years old by now.

Can't we do better than forcing the world to permanently add that

using std::name;

for essentially any call to a function in namespace std?

Can't [namespace.std] be extended to allow function overloads as a kin to the
(allowed) partial class temlate specialisations? With the same caveats? Must
use user type, must actually work as the std versions?

Or, alternatively, can't

int i, j;
swap(i, j);

automagically find the std version?

Thanks,
Marc
--
Marc Mutz <***@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt, C++ and OpenGL Experts
--
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/201705022023.38635.marc.mutz%40kdab.com.
Ville Voutilainen
2017-05-02 19:31:49 UTC
Permalink
Post by Marc Mutz
Can't [namespace.std] be extended to allow function overloads as a kin to the
(allowed) partial class temlate specialisations? With the same caveats? Must
use user type, must actually work as the std versions?
..must be an overload of an existing function specified by the
library, must not change
any overload resolution results, I think. Something like that makes
sense to me when I
vaguely remember the days when I was a mere language user, while still
remembering
why the restrictions that we currently have are there.
Post by Marc Mutz
Or, alternatively, can't
int i, j;
swap(i, j);
automagically find the std version?
I bet that idea breaks some code somewhere.
--
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/CAFk2RUb2QCim3QPwNCGrvitfMv3S5EOaMtRSESus4uALQ7kxqQ%40mail.gmail.com.
Nicol Bolas
2017-05-02 22:09:06 UTC
Permalink
Post by Marc Mutz
* technical term, not judgmental
Hi,
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#229 motivates
partial function specialisation for solving the problem that the world outside
namespace std has to write
using std::swap;
swap(a, b);
(where swap() is a stand-in for any std function, incl. e.g. sin(),
for_each()). They can't just write
std::swap(a, b);
because class authors are forbidden by [namespace.std] to add new declarations
(that includes function overloads) to namespace std, even though they are
allowed to partically specialise class templates there. You cannot partially
specialise function templates, you need to overload them. But you can't. Not
in namespace std. So users need to activate ADL, while still allowing falling
back to the std version, thus the using declaration + unqualified name lookup.
I don't know when I learned about this the first time, but I do know from
where: Scott Meyer's books. Those are ~20 years old by now.
Can't we do better than forcing the world to permanently add that
using std::name;
for essentially any call to a function in namespace std?
It's not "any call". It's only for calls where users would reasonably be
expected to provide overloads for their types. So that means just `swap`.

And `begin` and `end`. And `cbegin` and `cend`. And `rbegin/rend`. And
`crbegin/crend`. And `size` ;)

The problem really is that we want interfaces to allow 3 things though the
same syntax, and C++ doesn't really support this combination very well:

1) Users can specify the behavior of these functions via member functions.

2) Users can specify the behavior of these functions for types via
functions that are not members of that type.

3) Fundamental types can have defaults applied.

The problem is really #1 and #3. Both of these are important, and both of
them are provided by the same overload. And neither of them use ADL

The solution to this insanity was supposed to be unified function call
syntax, but the standards committee took a knee on that and refuses to even
think about fixing it anymore.

So no, it's not gonna be fixed.

Or, alternatively, can't
Post by Marc Mutz
int i, j;
swap(i, j);
automagically find the std version?
Even ignoring what breakage that could cause, it would still not be enough.
Remember that #1 is implemented through the same mechanism: `std::swap`
calling the type's member function. So if you have a type that provides a
member `swap` but it doesn't have a `swap` at namespace scope (and let's be
frank, it's *stupid* to have both), you still need `using std::swap` to get
an unqualified call to `swap` to call the member function.

Again, UFC would have fixed this, but we cannot have nice things in C++.
--
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/dbbdbc10-3f83-48ea-9a78-beb179bd113a%40isocpp.org.
Tony V E
2017-05-02 23:30:25 UTC
Permalink
Post by Nicol Bolas
Post by Marc Mutz
* technical term, not judgmental
Hi,
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#229 motivates
partial function specialisation for solving the problem that the world outside
namespace std has to write
using std::swap;
swap(a, b);
(where swap() is a stand-in for any std function, incl. e.g. sin(),
for_each()). They can't just write
std::swap(a, b);
because class authors are forbidden by [namespace.std] to add new declarations
(that includes function overloads) to namespace std, even though they are
allowed to partically specialise class templates there. You cannot partially
specialise function templates, you need to overload them. But you can't. Not
in namespace std. So users need to activate ADL, while still allowing falling
back to the std version, thus the using declaration + unqualified name lookup.
I don't know when I learned about this the first time, but I do know from
where: Scott Meyer's books. Those are ~20 years old by now.
Can't we do better than forcing the world to permanently add that
using std::name;
for essentially any call to a function in namespace std?
It's not "any call". It's only for calls where users would reasonably be
expected to provide overloads for their types. So that means just `swap`.
And `begin` and `end`. And `cbegin` and `cend`. And `rbegin/rend`. And
`crbegin/crend`. And `size` ;)
And we keep adding to the list, with no end in sight.
Post by Nicol Bolas
The problem really is that we want interfaces to allow 3 things though the
1) Users can specify the behavior of these functions via member functions.
2) Users can specify the behavior of these functions for types via
functions that are not members of that type.
3) Fundamental types can have defaults applied.
The problem is really #1 and #3. Both of these are important, and both of
them are provided by the same overload. And neither of them use ADL
The solution to this insanity was supposed to be unified function call
syntax, but the standards committee took a knee on that and refuses to even
think about fixing it anymore.
So no, it's not gonna be fixed.
There are many potential fixes. One simple one that just doesn't look
"elegant":

namespace std {
template<typename T>
auto foo(T t) { return std_foo(t); } // plus forwarding, etc
}

Users call std::foo(),
Extenders implement std_foo() for their type(s). Found by ADL.

This is basically an application of Herb's "Non virtual interface" pattern
(ie make virtuals private http://www.gotw.ca/publications/mill18.htm), just
at a different level.

Tony
Post by Nicol Bolas
Or, alternatively, can't
Post by Marc Mutz
int i, j;
swap(i, j);
automagically find the std version?
Even ignoring what breakage that could cause, it would still not be
`std::swap` calling the type's member function. So if you have a type that
provides a member `swap` but it doesn't have a `swap` at namespace scope
(and let's be frank, it's *stupid* to have both), you still need `using
std::swap` to get an unqualified call to `swap` to call the member function.
Again, UFC would have fixed this, but we cannot have nice things in C++.
--
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/dbbdbc10-3f83-48ea-
9a78-beb179bd113a%40isocpp.org
<https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/dbbdbc10-3f83-48ea-9a78-beb179bd113a%40isocpp.org?utm_medium=email&utm_source=footer>
.
--
Be seeing you,
Tony
--
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/CAOHCbisJiEuiC%2BwRFS5fMxsejUFiKzwxt8_DWMOD%2B%2BafBjFYmQ%40mail.gmail.com.
Arthur O'Dwyer
2017-05-03 02:08:11 UTC
Permalink
Post by Tony V E
Post by Nicol Bolas
Post by Marc Mutz
* technical term, not judgmental
Hi,
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#229 motivates
partial function specialisation for solving the problem that the world outside
namespace std has to write
using std::swap;
swap(a, b);
[...]
Can't we do better [...] ?
It's not "any call". It's only for calls where users would reasonably be
expected to provide overloads for their types. So that means just `swap`.
And `begin` and `end`. And `cbegin` and `cend`. And `rbegin/rend`. And
`crbegin/crend`. And `size` ;)
And we keep adding to the list, with no end in sight.
[...]
There are many potential fixes. One simple one that just doesn't look
namespace std {
template<typename T>
auto foo(T t) { return std_foo(t); } // plus forwarding, etc
}
Users call std::foo(),
Extenders implement std_foo() for their type(s). Found by ADL.
Eric Niebler has done some proposal-work in this area. I think I've also
chatted with Vicente Botet and Eric Fiselier on the subject. The search
term you're looking for is "customization points."
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
IMHO, the most elegant solution (basically Eric Niebler's proposal) would
involve MASSIVE breakage of old code and is thus suited only for "std2".

namespace std2 {
inline auto swap = [](auto& x, auto& y) constexpr -> void {
if constexpr (__has_adl_swap(x, y)) {
swap(x, y);
} else if constexpr (__has_swap_member_function(x, y)) {
x.swap(y);
} else {
auto temp = x;
x = std::move(y);
y = std::move(temp);
}
};
} // namespace std2

(My intention with the __has_adl_swap and __has_swap_member_function
pseudo-traits is that they should return constexpr true iff the
corresponding expressions are well-formed.)

Notice that because this "std2::swap" is an object, not a function:
- you can pass it to an STL function template without any hassle (as
opposed to the punctuation soup of std::less<>{})
- there is no temptation to "specialize" it via reopening namespace std2
(as there is, dangerously, with std::swap)
- there is no temptation to "overload" it via reopening namespace std2 (as
there is, fatally, with std::swap)

Notice also that:
- it can swap heterogeneous types: I have not thought deeply about this
aspect but I don't immediately see anything wrong with it
- it automagically works with your x.swap(y) member functions, thus you can
avoid implementing ADL swap if you want (as opposed to today's best
practice: implementing ADL-swap-in-terms-of-member-swap)

–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/50d6b1fa-618f-42c4-893e-3c20a855ea80%40isocpp.org.
Nicol Bolas
2017-05-03 02:26:18 UTC
Permalink
Post by Arthur O'Dwyer
Post by Tony V E
Post by Nicol Bolas
Post by Marc Mutz
* technical term, not judgmental
Hi,
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_closed.html#229 motivates
partial function specialisation for solving the problem that the world outside
namespace std has to write
using std::swap;
swap(a, b);
[...]
Can't we do better [...] ?
It's not "any call". It's only for calls where users would reasonably be
expected to provide overloads for their types. So that means just `swap`.
And `begin` and `end`. And `cbegin` and `cend`. And `rbegin/rend`. And
`crbegin/crend`. And `size` ;)
And we keep adding to the list, with no end in sight.
[...]
There are many potential fixes. One simple one that just doesn't look
namespace std {
template<typename T>
auto foo(T t) { return std_foo(t); } // plus forwarding, etc
}
Users call std::foo(),
Extenders implement std_foo() for their type(s). Found by ADL.
Eric Niebler has done some proposal-work in this area. I think I've also
chatted with Vicente Botet and Eric Fiselier on the subject. The search
term you're looking for is "customization points."
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
IMHO, the most elegant solution (basically Eric Niebler's proposal) would
involve MASSIVE breakage of old code and is thus suited only for "std2".
namespace std2 {
inline auto swap = [](auto& x, auto& y) constexpr -> void {
if constexpr (__has_adl_swap(x, y)) {
swap(x, y);
} else if constexpr (__has_swap_member_function(x, y)) {
x.swap(y);
} else {
auto temp = x;
x = std::move(y);
y = std::move(temp);
}
};
} // namespace std2
I'm not sure I entirely agree with the specific implementation, but this
kind of thing really sounds like something that ought to be supported in
the language in some way. I justify that by pointing out that the language
actually does support this in two places: range-based `for` and structured
binding, in searching for `begin/end` and `get`, respectively.

Note that in both cases, there is a also a default version. Ranged-based
`for` has specialized behavior for C++ array types, and structured binding
has special behavior for both arrays and publicly accessible structures.

This is also why I don't agree with the specific implementation, since
non-member functions have overriding priority over members. By contrast,
both `for` and structured binding give members priority.

If language features like range-based `for` and structured binding can do
this specific kind of thing, then it seems clear that users of the language
ought to be able to set such things up as well. And not with ad-hoc
solutions like the above, but with some kind of real language feature.

Maybe to avoid the UFC issues, it would work like this. We provide a way to
call a function (which is distinct from regular call syntax, thus pacifying
the anti-UFC people) where you specify at the call cite that you're using
the special lookup rules (check members, then check ADL, and if neither of
them work, use the default). And you provide a way to define a function
which represents one of the default cases when calling via these special
lookup rules (otherwise, it acts like a regular function).
Post by Arthur O'Dwyer
(My intention with the __has_adl_swap and __has_swap_member_function
pseudo-traits is that they should return constexpr true iff the
corresponding expressions are well-formed.)
- you can pass it to an STL function template without any hassle (as
opposed to the punctuation soup of std::less<>{})
Or we could just have lifting lambdas and deal with it that way. Whatever
happened to P0119, anyway?
Post by Arthur O'Dwyer
- there is no temptation to "specialize" it via reopening namespace std2
(as there is, dangerously, with std::swap)
- there is no temptation to "overload" it via reopening namespace std2 (as
there is, fatally, with std::swap)
A good language feature for customization points would remove such
temptations as well.
--
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/ede33ec9-cd19-4729-8f32-7045622830ff%40isocpp.org.
Arthur O'Dwyer
2017-05-03 03:16:28 UTC
Permalink
Post by Nicol Bolas
Post by Arthur O'Dwyer
Eric Niebler has done some proposal-work in this area. I think I've also
chatted with Vicente Botet and Eric Fiselier on the subject. The search
term you're looking for is "customization points."
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
IMHO, the most elegant solution (basically Eric Niebler's proposal) would
involve MASSIVE breakage of old code and is thus suited only for "std2".
namespace std2 {
inline auto swap = [](auto& x, auto& y) constexpr -> void {
if constexpr (__has_adl_swap(x, y)) {
swap(x, y);
} else if constexpr (__has_swap_member_function(x, y)) {
x.swap(y);
} else {
auto temp = x;
x = std::move(y);
y = std::move(temp);
}
};
} // namespace std2
I'm not sure I entirely agree with the specific implementation, but this
kind of thing really sounds like something that ought to be supported in
the language in some way. I justify that by pointing out that the language
actually does support this in two places: range-based `for` and structured
binding, in searching for `begin/end` and `get`, respectively.
FWIW: Structured binding actually searches for a whole mess of names,
including `tuple_size` and `tuple_element` (both of which it looks up *only*
in namespace std). So IMO "structured binding" should be an item on the
ever-growing list of "ill-advised places where a customization-point
feature was clearly needed but never actually designed," and *not* on the
list of "nice things to copy the design of."


[...] This is also why I don't agree with the specific implementation,
Post by Nicol Bolas
since non-member functions have overriding priority over members. By
contrast, both `for` and structured binding give members priority.
That's fair. Consider the specific implementation hereby amended to prefer
member x.swap(y) over non-member swap(x,y).
Post by Nicol Bolas
If language features like range-based `for` and structured binding can do
this specific kind of thing, then it seems clear that users of the language
ought to be able to set such things up as well. And not with ad-hoc
solutions like the above, but with some kind of real language feature.
We agree that regular users ought to be able to set up customization points
for their own stuff; indeed, basically every library in existence *already*
has to solve the customization-point problem one way or another. The
problem with making customization points like std::swap into "language
features" is that (AFAICT) that seems to point in the direction of a
solution that is *more* special-cased and *harder* for regular users to
adapt for their own libraries.

If I can solve the problem with an idiom that relies only on existing C++17
language features, then surely it would be a bad idea to come up with an
idiom that relies on non-existent language features; especially if we then
had to *propose* those language features and ram them through EWG motivated
only by this new swap idiom?


Maybe to avoid the UFC issues, it would work like this. We provide a way to
Post by Nicol Bolas
call a function (which is distinct from regular call syntax, thus pacifying
the anti-UFC people) where you specify at the call site that you're using
the special lookup rules (check members, then check ADL, and if neither of
them work, use the default). And you provide a way to define a function
which represents one of the default cases when calling via these special
lookup rules (otherwise, it acts like a regular function).
I have no really smart things to contribute here, but I'll note in passing
that this sounds like the sort of thing that reflection
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0194r3.html>
might be good at; e.g. we could write a template my::specialcall taking a
compile-time string and using reflection to figure out whether any member
function by that name actually existed —

my::specialcall<"swap"sl
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4121.pdf>>(x, y);

— except for the above's high probability of breaking cross-referencing
utilities like Kythe by hiding function names inside things that look like
arbitrary string values. (Which might already be a known issue blocking
reflection, for all I know.)
Post by Nicol Bolas
Post by Arthur O'Dwyer
- you can pass it to an STL function template without any hassle (as
opposed to the punctuation soup of std::less<>{})
Or we could just have lifting lambdas and deal with it that way. Whatever
happened to P0119, anyway?
No idea. I've been toying with the idea of writing a real proposal for the
[]foo syntax for Albuquerque, but I've got too many other things going on
to actually get started on that.
Post by Nicol Bolas
- there is no temptation to "specialize" it via reopening namespace std2
Post by Arthur O'Dwyer
(as there is, dangerously, with std::swap)
- there is no temptation to "overload" it via reopening namespace std2
(as there is, fatally, with std::swap)
A good language feature for customization points would remove such
temptations as well.
Yes; or to put it equivalently, "If a [language] feature does *not* remove
such temptations, then it is *not* good." We agree on this. I merely
listed those bullet points to demonstrate that my/Eric's proposed feature
isn't conspicuously not-good. (But logically, it might be not-good for some
other reason. "Removing such temptations" is a *necessary* but
*insufficient* condition for goodness.)

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/CADvuK0%2BdnpNXSaJUvgGf%2BV3XxTAnzYR7V4x-RbMu5Eg3v5f6LQ%40mail.gmail.com.
Nicol Bolas
2017-05-03 04:13:16 UTC
Permalink
Post by Arthur O'Dwyer
Post by Nicol Bolas
Post by Arthur O'Dwyer
Eric Niebler has done some proposal-work in this area. I think I've also
chatted with Vicente Botet and Eric Fiselier on the subject. The search
term you're looking for is "customization points."
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html
IMHO, the most elegant solution (basically Eric Niebler's proposal)
would involve MASSIVE breakage of old code and is thus suited only for
"std2".
namespace std2 {
inline auto swap = [](auto& x, auto& y) constexpr -> void {
if constexpr (__has_adl_swap(x, y)) {
swap(x, y);
} else if constexpr (__has_swap_member_function(x, y)) {
x.swap(y);
} else {
auto temp = x;
x = std::move(y);
y = std::move(temp);
}
};
} // namespace std2
I'm not sure I entirely agree with the specific implementation, but this
kind of thing really sounds like something that ought to be supported in
the language in some way. I justify that by pointing out that the language
actually does support this in two places: range-based `for` and structured
binding, in searching for `begin/end` and `get`, respectively.
FWIW: Structured binding actually searches for a whole mess of names,
including `tuple_size` and `tuple_element` (both of which it looks up
*only* in namespace std). So IMO "structured binding" should be an item
on the ever-growing list of "ill-advised places where a customization-point
feature was clearly needed but never actually designed," and *not* on the
list of "nice things to copy the design of."
To be honest... how else *could* it work? Oh sure, you might have found
some way to make `tuple_size` be a `constexpr` function or something
(though how you pass a parameter of type `E` to allow ADL without actually
having to *create* an object of that type would be an interesting trick).
But `tuple_element` has got to resolve to a *type*. And ADL just doesn't
exist for meta-functions.

Now this actually argues strongly for a more comprehensive approach to
customization points as a language feature. One that can include both
runtime and *compile-time* function customization. This would essentially
allow a user to invoke the rules of ADL to look up the exact template being
instantiated.

[...] This is also why I don't agree with the specific implementation,
Post by Arthur O'Dwyer
Post by Nicol Bolas
since non-member functions have overriding priority over members. By
contrast, both `for` and structured binding give members priority.
That's fair. Consider the specific implementation hereby amended to prefer
member x.swap(y) over non-member swap(x,y).
Post by Nicol Bolas
If language features like range-based `for` and structured binding can do
this specific kind of thing, then it seems clear that users of the language
ought to be able to set such things up as well. And not with ad-hoc
solutions like the above, but with some kind of real language feature.
We agree that regular users ought to be able to set up customization
points for their own stuff; indeed, basically every library in existence
*already* has to solve the customization-point problem one way or
another. The problem with making customization points like std::swap into
"language features" is that (AFAICT) that seems to point in the direction
of a solution that is *more* special-cased and *harder* for regular users
to adapt for their own libraries.
If I can solve the problem with an idiom that relies only on existing
C++17 language features, then surely it would be a bad idea to come up with
an idiom that relies on non-existent language features; especially if we
then had to *propose* those language features and ram them through EWG
motivated only by this new swap idiom?
Um, no, it wouldn't be a bad idea at all.

Consider the code you just showed me. I pointed out a deficiency in it: the
fact that it works the wrong way, relative to the hard-coded customization
points. And that was written by an actual C++ expert, which means that it's
very easy for a novice to get this idiom wrong. And a novice is far less
likely to realize they got it wrong. After all, in 99% of the cases, it'll
still work. It just has a subtle bug in it, which is worse in many respects
than being straight-up broken.

Having all customization points work the same way is really important.
Relying on everyone to use an idiom like this in exactly the same way is
essentially begging to fail. Oh sure, the standard library can mandate the
behavior of its customization points. But users need to be able to build
their own customization points, and to do so in a way that works just like
standard library ones.

And that's assuming that users are clever enough to implement
`__has_adl_swap` and `__has_swap_member_function` *correctly* in the first
place. I'm no metaprogramming expert, but I have no idea how to even *begin*
implementing the ADL check.

Just as Boost.Lambda helped prove that lambdas ought to be a language
feature, I think the weaknesses of this idiom (which is the best
customization interface we've been able to create thus far) helps show that
this ought to be a language feature too.
--
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/991619cd-f935-43e0-85a5-b417cd6afe58%40isocpp.org.
Arthur O'Dwyer
2017-05-03 05:17:30 UTC
Permalink
Post by Nicol Bolas
Post by Arthur O'Dwyer
FWIW: Structured binding actually searches for a whole mess of names,
including `tuple_size` and `tuple_element` (both of which it looks up
*only* in namespace std). So IMO "structured binding" should be an item
on the ever-growing list of "ill-advised places where a customization-point
feature was clearly needed but never actually designed," and *not* on
the list of "nice things to copy the design of."
To be honest... how else *could* it work? Oh sure, you might have found
some way to make `tuple_size` be a `constexpr` function or something
(though how you pass a parameter of type `E` to allow ADL without actually
having to *create* an object of that type would be an interesting trick).
But `tuple_element` has got to resolve to a *type*. And ADL just doesn't
exist for meta-functions.
In the case of structured binding, the Right Thing would have been to not
rely on tuple_element at all. They could easily have made

auto [x,y] = t;

equivalent to

auto __t = t;
auto&& x = get<0>(__t); // with compiler magic to make decltype(x)
appear as a non-reference type, of course
auto&& y = get<1>(__t);

without any reference to tuple_element. (And notice that the rewrite
doesn't explicitly use tuple_size, either. tuple_size is needed only to
generate annoying compiler diagnostics in the case that t "has" more than 2
elements. How to bind only the first K ordinates of a tuple and discard the
rest is a perennial topic of discussion on this mailing list.)


[...] This is also why I don't agree with the specific implementation,
Post by Nicol Bolas
Post by Arthur O'Dwyer
Post by Nicol Bolas
since non-member functions have overriding priority over members. By
contrast, both `for` and structured binding give members priority.
That's fair. Consider the specific implementation hereby amended to
prefer member x.swap(y) over non-member swap(x,y).
[...] Consider the code you just showed me. I pointed out a deficiency in
it: the fact that it works the wrong way, relative to the hard-coded
customization points. And that was written by an actual C++ expert, which
means that it's very easy for a novice to get this idiom wrong. And a
novice is far less likely to realize they got it wrong. After all, in 99%
of the cases, it'll still work. It just has a subtle bug in it, which is
worse in many respects than being straight-up broken.
I'd argue that if we had proper customization points (e.g. these
inline-variable-based ones), then the difference between "subtle bug" and
"perfectly correct" would become academic — I'm inclined to go further and
say "would become nonsensical." The "subtle bug" you identified would cause
problems during execution if-and-only-if:

- The user provided x.swap(y) with functionality 1
- The user provided an ADL swap(x, y) with functionality 2, observably
different from functionality 1
- The user called std2::swap(x, y) expecting to receive functionality 1

I claim that bullet points #2 and #3 are deep into "well, what did you
expect to happen?"-land, even in C++03. When you provide two swap
functions, you'd better make darn sure that their behaviors are identical;
and even if by accident their behaviors are different, you'd better not
write code that *relies* on that bug!

If we had std2::swap today, there would be no reason for anyone to
implement ADL swap(x, y) ever again; not even to implement it in terms of
x.swap(y). Forget having two swap functions with different behaviors;
there'd be no reason to provide two swap functions *period*. You'd simply
implement your swap function as x.swap(y), and call it as std2::swap(x, y),
and that would be all.


Having all customization points work the same way is really important.
Post by Nicol Bolas
Relying on everyone to use an idiom like this in exactly the same way is
essentially begging to fail. Oh sure, the standard library can mandate the
behavior of its customization points. But users need to be able to build
their own customization points, and to do so in a way that works just like
standard library ones.
Just like the state of C++03 iterators (begin() and end()), right? I claim
that users who need to implement their own iterators can in fact do so, and
users who need to implement their own customization points will in fact be
able to do so. Just as with iterators, the user's solution will probably
involve some cut-and-paste from the standard library. (Today we can't
cut-and-paste customization points from the standard library because the
standard library has no solution for customization points. But if we added
std2::swap, then it would have *a* solution.)

And that's assuming that users are clever enough to implement
Post by Nicol Bolas
`__has_adl_swap` and `__has_swap_member_function` *correctly* in the
first place. I'm no metaprogramming expert, but I have no idea how to even
*begin* implementing the ADL check.
I think it's easy, as long as you don't care about swap functions that show
up in the *global* namespace (not in the ADL namespace in question) after
the inclusion of <std2_swap>. (It's unclear to me whether functions in the
global namespace ought to be considered "ADL" or not. Anyway, this is
another case of "well, what did you *expect* to happen?".)
https://wandbox.org/permlink/vrqWSFw7mI6xi6sY
Post by Nicol Bolas
Just as Boost.Lambda helped prove that lambdas ought to be a language
feature, I think the weaknesses of this idiom (which is the best
customization interface we've been able to create thus far) helps show that
this ought to be a language feature too.
That's a fair analogy; but I still can't picture what kind of core language
feature would help here. With lambdas it was pretty obvious what the core
feature was; the only bikeshedding was over the spelling of []. With
customization points, it's not real clear *what* we're trying to spell in
the first place. A new call syntax? A new way of doing name lookup? A
new way of defining functions?

Here's an idea inspired by that "core language feature" starting point.
Here *customization_point* is a contextual keyword that means "Compiler,
please wrap this function's body in boilerplate so that it'll call
<firstarg>.<functionname>(<otherargs>) if possible; or delegate to the
<functionname> found by ADL if possible; or else fall back on this
function's body as the default implementation." (The function's body might
reasonably be =delete'd.)

namespace std3 {
template<class T>
auto swap(T& a, T& b) *customization_point* {
auto temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}
} // namespace std3

std3::swap(i, j); // implicitly attempts to call i.swap(j) instead, if
it exists

using std3::swap;
swap(m, n); // plain old unqualified name lookup; but it might find
std3::swap,
// in which case we implicitly attempt to call m.swap(n) instead,
and so on

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/CADvuK0Kj%3D6CpZ0Ceoj-V7Przk1S7v8%3DEgN0VbDis6EmaaOkruA%40mail.gmail.com.
T. C.
2017-05-03 10:16:24 UTC
Permalink
Post by Arthur O'Dwyer
Post by Nicol Bolas
Post by Arthur O'Dwyer
FWIW: Structured binding actually searches for a whole mess of names,
including `tuple_size` and `tuple_element` (both of which it looks up
*only* in namespace std). So IMO "structured binding" should be an item
on the ever-growing list of "ill-advised places where a customization-point
feature was clearly needed but never actually designed," and *not* on
the list of "nice things to copy the design of."
To be honest... how else *could* it work? Oh sure, you might have found
some way to make `tuple_size` be a `constexpr` function or something
(though how you pass a parameter of type `E` to allow ADL without actually
having to *create* an object of that type would be an interesting
trick). But `tuple_element` has got to resolve to a *type*. And ADL just
doesn't exist for meta-functions.
In the case of structured binding, the Right Thing would have been to not
rely on tuple_element at all. They could easily have made
auto [x,y] = t;
equivalent to
auto __t = t;
auto&& x = get<0>(__t); // with compiler magic to make decltype(x)
appear as a non-reference type, of course
auto&& y = get<1>(__t);
without any reference to tuple_element. (And notice that the rewrite
doesn't explicitly use tuple_size, either. tuple_size is needed only to
generate annoying compiler diagnostics in the case that t "has" more than
2 elements. How to bind only the first K ordinates of a tuple and discard
the rest is a perennial topic of discussion on this mailing list.)
And that magic will fail for at least one of tuple<int&>, tuple<int&&> and
tuple<int>. It can't provide the right answer for all three. Care to
explain how that is the Right Thing?

BTW, AFAIK the hardcoded begin/end/get lookup rules in the language are
impossible to implement exactly in plain C++. The ADL part can be done by
SFINAEing on the well-formedness of an unqualified call where the only
candidate found by ordinary lookup is a nonviable candidate. The tricky
part is actually the class member lookup, which goes for the member
interpretation if there's a member with the right name regardless of its
kind, type, or accessibility. I don't know of a way to check that for final
and union types.
--
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/0c1c2586-5311-4a2e-bea1-09cdab2d9707%40isocpp.org.
Arthur O'Dwyer
2017-05-03 10:47:16 UTC
Permalink
Post by T. C.
Post by Arthur O'Dwyer
Post by Nicol Bolas
Post by Arthur O'Dwyer
FWIW: Structured binding actually searches for a whole mess of names,
including `tuple_size` and `tuple_element` (both of which it looks up
*only* in namespace std). So IMO "structured binding" should be an
item on the ever-growing list of "ill-advised places where a
customization-point feature was clearly needed but never actually
designed," and *not* on the list of "nice things to copy the design
of."
To be honest... how else *could* it work? Oh sure, you might have found
some way to make `tuple_size` be a `constexpr` function or something
(though how you pass a parameter of type `E` to allow ADL without actually
having to *create* an object of that type would be an interesting
trick). But `tuple_element` has got to resolve to a *type*. And ADL
just doesn't exist for meta-functions.
In the case of structured binding, the Right Thing would have been to not
rely on tuple_element at all. They could easily have made
auto [x,y] = t;
equivalent to
auto __t = t;
auto&& x = get<0>(__t); // with compiler magic to make decltype(x)
appear as a non-reference type, of course
auto&& y = get<1>(__t);
without any reference to tuple_element. (And notice that the rewrite
doesn't explicitly use tuple_size, either. tuple_size is needed only to
generate annoying compiler diagnostics in the case that t "has" more
than 2 elements. How to bind only the first K ordinates of a tuple and
discard the rest is a perennial topic of discussion on this mailing list.)
And that magic will fail for at least one of tuple<int&>, tuple<int&&> and
tuple<int>. It can't provide the right answer for all three. Care to
explain how that is the Right Thing?
Hm. Today I have learned that "auto& [x,y] = t;" doesn't do what I thought
it did.
The "natural" thing for it to have done would have been to turn both x and y
into auto& (lvalue reference) types:

auto& __t = t;
auto& x = get<0>(__t); // no compiler magic necessary
auto& y = get<1>(__t);

But it turns out that it really does something ultra bizarre — namely, it
applies the compiler magic to *remove the referenceness of decltype(x)*
even though x does literally have reference semantics.

std::tuple<int> t1(1);
auto& [r1] = t1;
static_assert(std::is_same_v<decltype(r1), int>); // r1 is not a
reference...
static_assert(!std::is_reference_v<decltype(r1)>); // 100% not a
reference...
r1 = 2;
assert(t1 == std::tuple(2)); // ...yet it has reference semantics!

https://wandbox.org/permlink/W09BZU7VGJZpUSbH

If it had done the thing I had thought it would do (preserve the
reference-ness of the declaration), then there wouldn't have been any
problem.


Re-reading your objection, I think you might have been talking about
something else anyway. I see how the current state of auto [x] versus auto&
[x] versus auto&& [x] is a bit screwy; but I don't see the problem you're
seeing with std::tuple<int> versus std::tuple<int&> versus
std::tuple<int&&>. In those three cases, std::get<0>(__t) will return a
value with the appropriate degree of (rvalue-)referenceness, and there
won't be any problem as far as I'm aware.

–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/CADvuK0KQJmOzgAVLRUqf%2BFWqBsUZReFRhOznp8mkmQUR4-CSrg%40mail.gmail.com.
Thiago Macieira
2017-05-03 13:43:35 UTC
Permalink
But it turns out that it really does something ultra bizarre — namely, it
applies the compiler magic to *remove the referenceness of decltype(x)*
even though x does literally have reference semantics.
std::tuple<int> t1(1);
auto& [r1] = t1;
static_assert(std::is_same_v<decltype(r1), int>); // r1 is not a
reference...
static_assert(!std::is_reference_v<decltype(r1)>); // 100% not a
reference...
r1 = 2;
assert(t1 == std::tuple(2)); // ...yet it has reference semantics!
It is quite surprising, but you have to understand what a structured binding
is.

auto& [r1] = t1;

Does not create variable called t1 that references t1's first element.

Instead, it creates an unnamed variable that is a reference to t1. That
variable is a structure and it has r1 as its first element.

This is the only way you could do:

struct S {
int v : 31;
int s : 1;
} s;
auto &[value, sign] = s;

This compiles.
--
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/1763858.k1t25K7h46%40tjmaciei-mobl1.
T. C.
2017-05-03 15:48:41 UTC
Permalink
Post by Arthur O'Dwyer
Post by T. C.
Post by Arthur O'Dwyer
Post by Nicol Bolas
Post by Arthur O'Dwyer
FWIW: Structured binding actually searches for a whole mess of names,
including `tuple_size` and `tuple_element` (both of which it looks up
*only* in namespace std). So IMO "structured binding" should be an
item on the ever-growing list of "ill-advised places where a
customization-point feature was clearly needed but never actually
designed," and *not* on the list of "nice things to copy the design
of."
To be honest... how else *could* it work? Oh sure, you might have
found some way to make `tuple_size` be a `constexpr` function or something
(though how you pass a parameter of type `E` to allow ADL without actually
having to *create* an object of that type would be an interesting
trick). But `tuple_element` has got to resolve to a *type*. And ADL
just doesn't exist for meta-functions.
In the case of structured binding, the Right Thing would have been to
not rely on tuple_element at all. They could easily have made
auto [x,y] = t;
equivalent to
auto __t = t;
auto&& x = get<0>(__t); // with compiler magic to make decltype(x)
appear as a non-reference type, of course
auto&& y = get<1>(__t);
without any reference to tuple_element. (And notice that the rewrite
doesn't explicitly use tuple_size, either. tuple_size is needed only to
generate annoying compiler diagnostics in the case that t "has" more
than 2 elements. How to bind only the first K ordinates of a tuple and
discard the rest is a perennial topic of discussion on this mailing list.)
And that magic will fail for at least one of tuple<int&>, tuple<int&&>
and tuple<int>. It can't provide the right answer for all three. Care to
explain how that is the Right Thing?
Hm. Today I have learned that "auto& [x,y] = t;" doesn't do what I
thought it did.
The "natural" thing for it to have done would have been to turn both x
auto& __t = t;
auto& x = get<0>(__t); // no compiler magic necessary
auto& y = get<1>(__t);
But it turns out that it really does something ultra bizarre — namely, it
applies the compiler magic to *remove the referenceness of decltype(x)*
even though x does literally have reference semantics.
std::tuple<int> t1(1);
auto& [r1] = t1;
static_assert(std::is_same_v<decltype(r1), int>); // r1 is not a
reference...
static_assert(!std::is_reference_v<decltype(r1)>); // 100% not a
reference...
r1 = 2;
assert(t1 == std::tuple(2)); // ...yet it has reference semantics!
https://wandbox.org/permlink/W09BZU7VGJZpUSbH
If it had done the thing I had thought it would do (preserve the
reference-ness of the declaration), then there wouldn't have been any
problem.
Re-reading your objection, I think you might have been talking about
something else anyway. I see how the current state of auto [x] versus auto&
[x] versus auto&& [x] is a bit screwy; but I don't see the problem you're
seeing with std::tuple<int> versus std::tuple<int&> versus
std::tuple<int&&>. In those three cases, std::get<0>(__t) will return a
value with the appropriate degree of (rvalue-)referenceness, and there
won't be any problem as far as I'm aware.
–Arthur
You have auto&& x = get<0>(AS_RVALUE(__t)); x is either int& or int&&,
depending on __t's type. What kind of "magic" will let decltype(x) get to
int, int&, and int&&?
--
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/9dd480f8-18bf-4a16-a3ea-892c53607273%40isocpp.org.
Barry Revzin
2017-05-03 13:12:36 UTC
Permalink
Post by Arthur O'Dwyer
That's a fair analogy; but I still can't picture what kind of core
language feature would help here. With lambdas it was pretty obvious what
the core feature was; the only bikeshedding was over the spelling of [].
With customization points, it's not real clear *what* we're trying to
spell in the first place. A new call syntax? A new way of doing name
lookup? A new way of defining functions?
Reaching back into the archives, the original range-based for loop proposal
used concepts as customization points:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2778.htm, which
would invoke std::Range<_RangeT>::begin(__range) and
std::Range<_RangeT>::end(__range).

Or we could just have lifting lambdas and deal with it that way. Whatever
Post by Arthur O'Dwyer
happened to P0119, anyway?
See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0382r0.html
--
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/13f995f2-f92c-4a73-a8d5-b7dea6395d20%40isocpp.org.
Nicol Bolas
2017-05-03 15:38:53 UTC
Permalink
Post by Arthur O'Dwyer
That's a fair analogy; but I still can't picture what kind of core
Post by Arthur O'Dwyer
language feature would help here. With lambdas it was pretty obvious what
the core feature was; the only bikeshedding was over the spelling of [].
With customization points, it's not real clear *what* we're trying to
spell in the first place. A new call syntax? A new way of doing name
lookup? A new way of defining functions?
Reaching back into the archives, the original range-based for loop
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2778.htm, which
would invoke std::Range<_RangeT>::begin(__range) and
std::Range<_RangeT>::end(__range).
Or we could just have lifting lambdas and deal with it that way. Whatever
Post by Arthur O'Dwyer
happened to P0119, anyway?
See: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0382r0.html
I guess I was referring to the wrong proposal. The one I was thinking of
made `[]funcname` generate a lambda, rather than the implicit nonsense
P0119 was trying to do.
--
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/f6c5ccf7-3dca-45ac-b19c-e1d016238d6f%40isocpp.org.
Vicente J. Botet Escriba
2017-05-04 00:54:14 UTC
Permalink
Post by Arthur O'Dwyer
That's a fair analogy; but I still can't picture what kind of core
language feature would help here. With lambdas it was pretty
obvious what the core feature was; the only bikeshedding was over
the spelling of []. With customization points, it's not real clear
/what/ we're trying to spell in the first place. A new call
syntax? A new way of doing name lookup? A new way of defining
functions?
Reaching back into the archives, the original range-based for loop
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2778.htm,
which would invoke std::Range<_RangeT>::begin(__range) and
std::Range<_RangeT>::end(__range).
Thanks for adding this old link. The calling interface should be even
more simple std2::range::begin(aRange) and std2::range::end(aRange) but
not simpler: std2::begin(aRange) and std2::end(aRange).

IMO, the std2 customization mechanism as proposed for the Range TS
doesn't scale for the C++ standard library and the approach cannot be
used by the users to define his own customization points. The last could
be seen as not a problem for the standard C++, but people must solve
concrete problems and they copy the C++ library design as it should be
the correct one, but they cannot in this case. See for example the
customization mechanism of Boost.Hana and many other generic libraries.
If they use ADL as customization point, they can not support C++
standard library types.

We will see more a more concepts very soon. In the same way we associate
a member function to a class, we should associate a customization point
to a concept and name it. There shouldn't be a confusion of which
customization point belongs to what concept. It is not the same
product_type::get than sum_type::get, container::size than memory::size.
begin/end is not something limited to ranges. When I say name it I mean
that the user must name the concept and the function name when she want
to call it and that the customization is done explicitly using this
concept name as well. This is not the design used in on the C++ standard
up to C++17 and will not change for C++20 with the current direction.

We would need more a more customization points if we continue to do
generic programming and not only on the C++ standard library. Having all
these customization points in a flat namespace don't scale, the
customization points become pseudo keywords. We need to scope them. We
have namespaces to manage with scope, and the standard library is not
using them (the exceptions of chrono and filesystem are domain
namespaces which of course is a very good use of namespaces). I expected
to have a std2::ranges namespace in the future STD2 to signal that
anything there was talking about customization points of the the Range
concept and the algorithms that work with this concept. Instead, IIUC we
will have again a flat std2 namespace without structure. I know that
this will make the names longer, but we have other C++ features that
help us, as namespace aliases. Language that don't have namespaces rely
on prefixes to ensure a unique meaning for a customization point. C++
is more powerful and we should use this power for the standard library.

I don't see the advantage of having implicit mappings that are based on
syntactical vraisemblance. Why a class defining begin() and end()
function members should become a Range if the functions don't support
the Range semantics. While sometimes it is possible to ensure almost all
the semantic concerns others it isn't so easy. At the end a syntactical
mapping has more liabilities than advantages as most of the short term
solutions.

I know that we want backward compatibility and any alternative
customization approach needs to address it. This doesn't mean that we
should base our future customization strategy on an approach ADL that
has a lot of problems, see [N1691].

I agree that we need a language feature to define customization points
and a way to customize them. Other languages have already do that with
success. C++ is a complex language and we don't want to add anything
that make it even more complex. I believe however that we really need to
solve this problem at the languages level. IMHO, C++0x addressed in part
this problematic but the C++ standard abandoned this direction and
people as Bjarne Stroustrup and many others don't want to consider any
proposal that has an explicit concept/customization points mapping. This
means that people are trying to solve the problem at the library level.
I believe that the proposed std2 customization mechanism has his merits
on the continuation of this direction. The approach solves already some
important problems, but the solution doesn't takes in account the
problems addressed in [N1691] and is more complex that the lambda user
is able to design. As Bjarne Stroustrup says, "Make simple thing simpler".

Sorry, but I don't have a language proposal. In the mean time I use an
alternative customization point mechanism (close to the one of
Boost.Hana) that is explicit in the calling side and explicit in the
customization side, but the customization solution is a little bit
cumbersome. I use it on some of my proposal as Nullable, Factories,
Product Type. I'm using the same approach also for other on going
proposal I'm working on, as e.g. for Sum Types, Ordinal types, Functors
or Monads. These proposals don't propose function object to represent
the user interface but this could be considered. IMHO, providing
function objects is orthogonal to the customization point approach and
the fact that the current STD2 approach requires the use of function
objects shouldn't be considered as a goal. As others have noted we have
some proposals on overloads sets that would make it very easy to provide
a function object associated to an overload set.

The current customization points could be adapted to this explicit
approach while preserving backward compatibility, as e.g. for Swappable,
Hashable and Range. I don't see these proposals as a final solution and
I hope these proposals could inspire some language solution(s) (some
kind of explicit namespace, partial template class specialization from
unrelated namespaces, traits, ...) or at least a better library solution.

Vicente

[N1691] Explicit Namespaces
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1691.html
--
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/3f13697d-a5d6-d802-68b6-291c79f0b041%40wanadoo.fr.
Matthew Fioravante
2017-05-05 17:54:16 UTC
Permalink
The swap(), begin(), end(), size(), etc... is an embarrassment.

- Its ugly to adding these using declarations.
- Its crazy error prone. Too easy to forget a using declaration.
- These functions cannot be called in single expression contexts like
decltype().

Why can't we just make std::swap() do all of the magic dispatching
internally? Then the convention is people always call std::swap(),
std::begin(), etc..
--
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/c76cb8b1-df82-48e7-a8dc-26f91f923cb6%40isocpp.org.
Nicol Bolas
2017-05-05 18:03:10 UTC
Permalink
Post by Matthew Fioravante
The swap(), begin(), end(), size(), etc... is an embarrassment.
- Its ugly to adding these using declarations.
- Its crazy error prone. Too easy to forget a using declaration.
- These functions cannot be called in single expression contexts like
decltype().
Why can't we just make std::swap() do all of the magic dispatching
internally? Then the convention is people always call std::swap(),
std::begin(), etc..
Because it would be a backwards-incompatible change. It also wouldn't help
users create similar interfaces that have similar 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/a3814105-494c-4d85-bedc-b05f5d165a10%40isocpp.org.
Ville Voutilainen
2017-05-05 18:28:50 UTC
Permalink
Post by Nicol Bolas
Post by Matthew Fioravante
The swap(), begin(), end(), size(), etc... is an embarrassment.
- Its ugly to adding these using declarations.
- Its crazy error prone. Too easy to forget a using declaration.
- These functions cannot be called in single expression contexts like
decltype().
Why can't we just make std::swap() do all of the magic dispatching
internally? Then the convention is people always call std::swap(),
std::begin(), etc..
Because it would be a backwards-incompatible change. It also wouldn't help
users create similar interfaces that have similar behavior.
There have been suggestions to add an adl_swap that does all this
"magic dispatching" internally.
--
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/CAFk2RUZ5Ku4s%3DayZ8z502AtLqfZrNSaHudE%2BYWeeGy-s5weYow%40mail.gmail.com.
Matthew Fioravante
2017-05-05 18:29:02 UTC
Permalink
Post by Nicol Bolas
Because it would be a backwards-incompatible change.
C++ has broken backwards compatibility before. Right now if you call
std::swap() on your object, unless its in std you'll get a default move
based swap operation. Suppose that was all of the sudden silently changed
to actually call your type's swap() operation if it has one. How bad would
that be? How much code would it break?

I consider swap() special in the same vein as move and copy. We've added
rules to change how those are called/elided in the past knowing it could
break code. If you write a swap() its suppose to do swapping, if it does
something else you get what you deserve. "Optimizing" std::swap() to call
your types custom swap if it has one does not sound like a bad thing to me.

The fact that essential library primitives like swap() and begin() require
expert level knowledge to use correctly is a complete and utter failure.*
Really, its just ****ing stupid.* ADL is not the proper tool for this. I'd
argue ADL is only really useful for operator overloading. Unless you put
swap(), begin(), etc.. to the global :: namespace, ADL will never work.
That's a non-starter too as third party libraries wanting to define
"customization points" such as begin() certainly cannot just put stuff in
the global namespace.

With have virtual functions for runtime dispatch, and this broken crap
interface for compile time dispatch. This is a serious bug that needs to be
fixed.
Post by Nicol Bolas
It also wouldn't help users create similar interfaces that have similar
behavior.
That's a secondary concern. If I want to write foo::fooify() customization
point in libfoo, I can lookup a tutorial to get all of the dispatching
correct. I only need to do this once for all of the thousands if not
millions of times my users will just need to say
foo::fooify(fooable_thing);. If I'm writing my own customization points,
I'm already an expert C++ developer. Furthermore, we could also consider
adding helper utilities for this in the standard library, even if they have
to be macros.
--
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/a7e2efc2-bbee-40ed-963b-42339bf0e21c%40isocpp.org.
Ville Voutilainen
2017-05-05 18:39:03 UTC
Permalink
Post by Matthew Fioravante
Post by Nicol Bolas
Because it would be a backwards-incompatible change.
C++ has broken backwards compatibility before. Right now if you call
That's not an open license to break compatibility whenever you feel like it.
Post by Matthew Fioravante
std::swap() on your object, unless its in std you'll get a default move
based swap operation. Suppose that was all of the sudden silently changed to
actually call your type's swap() operation if it has one. How bad would that
be? How much code would it break?
That needs to be explored. Such a change can lead to program breakage
if std::swap starts
calling swaps that weren't meant for swapping, and I have no idea
whether such beasts exist.
Post by Matthew Fioravante
I consider swap() special in the same vein as move and copy. We've added
rules to change how those are called/elided in the past knowing it could
break code. If you write a swap() its suppose to do swapping, if it does
something else you get what you deserve. "Optimizing" std::swap() to call
I don't deserve my working code to stop compiling just because you
have problems with
how std::swap is defined.
Post by Matthew Fioravante
The fact that essential library primitives like swap() and begin() require
expert level knowledge to use correctly is a complete and utter failure.
Really, its just ****ing stupid. ADL is not the proper tool for this. I'd
Well, we get very few bug reports about it, and even fewer proposals.
I know, most users
don't report bugs, but there's no indication that this is such a big 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/CAFk2RUbbFF7mZn6urZqZsCPwMWvHnwqn_gmuA3DLwpMQ0pX%3D8w%40mail.gmail.com.
Matthew Fioravante
2017-05-05 19:03:36 UTC
Permalink
Post by Ville Voutilainen
Post by Matthew Fioravante
Post by Nicol Bolas
Because it would be a backwards-incompatible change.
C++ has broken backwards compatibility before. Right now if you call
That's not an open license to break compatibility whenever you feel like it.
Post by Matthew Fioravante
std::swap() on your object, unless its in std you'll get a default move
based swap operation. Suppose that was all of the sudden silently
changed to
Post by Matthew Fioravante
actually call your type's swap() operation if it has one. How bad would
that
Post by Matthew Fioravante
be? How much code would it break?
That needs to be explored. Such a change can lead to program breakage
if std::swap starts
calling swaps that weren't meant for swapping, and I have no idea
whether such beasts exist.
It would have to be a case where someone explicitly calls std::swap()
already on their type and the type has a swap() method which is doing
something other than swap entirely. That already sounds like an extremely
rare (and poorly designed) unicorn to begin with.

Lets also not forget the standard convention of implementing swap() has
been around since the beginning of C++ and has been a standard practice
recommended in any C++ tutorial, book, or class for decades. I can't
imagine so many people really abuse swap() for something else.

Certainly it does need to be explored. I'm not suggesting we go and break
the world without a proper study. The problem is that a lot of times the
specter of breaking backwards compatibility even for an extremely rare and
improbable edge case shuts out the possibility of even discussing a change
entirely from the start.
Post by Ville Voutilainen
Post by Matthew Fioravante
I consider swap() special in the same vein as move and copy. We've added
rules to change how those are called/elided in the past knowing it could
break code. If you write a swap() its suppose to do swapping, if it does
something else you get what you deserve. "Optimizing" std::swap() to
call
I don't deserve my working code to stop compiling just because you
have problems with
how std::swap is defined.
I certainly doubt any of the code you personally have wrote, or anyone else
in this thread would be broken by such a change.

Its not the same as for example fixing min() and max(). That would be a
great change, but it almost certainly would break a lot of code and
therefore can never be fixed without an STL rewrite.
Post by Ville Voutilainen
Post by Matthew Fioravante
The fact that essential library primitives like swap() and begin()
require
Post by Matthew Fioravante
expert level knowledge to use correctly is a complete and utter failure.
Really, its just ****ing stupid. ADL is not the proper tool for this.
I'd
Well, we get very few bug reports about it, and even fewer proposals.
I know, most users
don't report bugs, but there's no indication that this is such a big problem.
Its an expert level problem. How many expert level users are really aware
of all of the caveats? How many of that subset actually file bugs, post in
this forum, or somehow otherwise contribute to standardization? How many
people just call x.begin() in their code (raising my own hand here) and
completely ignore this broken and complicated ADL business?

How many people have needed to do decltype(begin(x)) and then really saw
how terrible this API is?
--
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/9539ea6c-14b4-41e7-99f0-6bb6cc4d947b%40isocpp.org.
Nicol Bolas
2017-05-05 19:58:12 UTC
Permalink
Post by Matthew Fioravante
Post by Nicol Bolas
Because it would be a backwards-incompatible change.
C++ has broken backwards compatibility before. Right now if you call
std::swap() on your object, unless its in std you'll get a default move
based swap operation. Suppose that was all of the sudden silently changed
to actually call your type's swap() operation if it has one. How bad would
that be? How much code would it break?
I consider swap() special in the same vein as move and copy. We've added
rules to change how those are called/elided in the past knowing it could
break code. If you write a swap() its suppose to do swapping, if it does
something else you get what you deserve. "Optimizing" std::swap() to call
your types custom swap if it has one does not sound like a bad thing to me.
The fact that essential library primitives like swap() and begin() require
expert level knowledge to use correctly is a complete and utter failure.*
Really, its just ****ing stupid.* ADL is not the proper tool for this.
I'd argue ADL is only really useful for operator overloading. Unless you
put swap(), begin(), etc.. to the global :: namespace, ADL will never work.
That's a non-starter too as third party libraries wanting to define
"customization points" such as begin() certainly cannot just put stuff in
the global namespace.
With have virtual functions for runtime dispatch, and this broken crap
interface for compile time dispatch. This is a serious bug that needs to be
fixed.
I don't disagree on the importance of customization points. I don't
disagree with the need to have them work.

That alone however *cannot* justify doing it in a backwards-incompatible
way.

It also wouldn't help users create similar interfaces that have similar
Post by Matthew Fioravante
Post by Nicol Bolas
behavior.
That's a secondary concern.
I strongly disagree. It's like saying that we need to have ranges in the
standard library, but it's a secondary concern to provide tools to help
people write standard library compatible ranges. What good is it to have
standard idioms if writing idiom-compatible constructs is so complex/arcane
that users can not reasonably be expected to define them on their own?

Writing a standard library compatible range should not be hard. Writing a
standard library compatible customization point should also not be hard.
This should be a primary concern of any solution to this problem. Once we
standardize the concept of "customization point", people will and should
want to do it in a way that is compatible with the standard library. If we
make that simple, then we allow them to do so.

If we make the idiom complex or arcane, all we do is create dozens of
headaches down the road.
Post by Matthew Fioravante
If I want to write foo::fooify() customization point in libfoo, I can
lookup a tutorial to get all of the dispatching correct. I only need to do
this once for all of the thousands if not millions of times my users will
just need to say foo::fooify(fooable_thing);. If I'm writing my own
customization points, I'm already an expert C++ developer.
That kind of thinking is what gets us `std::enable_if`. "If I want to do
some SFINAE-style stuff, then I'm already an expert C++ developer".

Utter nonsense. By adding appropriate things to the language, we give
novices the power to do the things that experts do. We take arcane idioms
and bring them to the masses.

When we get concepts, you'll see the number of people using SFINAE increase
dramatically. Why? Because it makes it a real part of the language, not
some arcane hack. Users have needs that SFINAE could solve, but they don't
use it because it's needlessly difficult and over-complicated. The same
goes here. Creating a customization points should not be something that
requires being an expert C++ developer. Users want to be able to do these
things, but as of yet, there is no good, simple solution for it.

Furthermore, we could also consider adding helper utilities for this in the
Post by Matthew Fioravante
standard library, even if they have to be macros.
Yes, don't bother with a nice, neat language feature. It's much better to
do this sort of stuff as a macro.
--
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/75ea01c3-e829-4ba6-9311-189748f73354%40isocpp.org.
Matthew Fioravante
2017-05-05 20:40:43 UTC
Permalink
Post by Nicol Bolas
Post by Matthew Fioravante
Post by Nicol Bolas
Because it would be a backwards-incompatible change.
C++ has broken backwards compatibility before. Right now if you call
std::swap() on your object, unless its in std you'll get a default move
based swap operation. Suppose that was all of the sudden silently changed
to actually call your type's swap() operation if it has one. How bad would
that be? How much code would it break?
I consider swap() special in the same vein as move and copy. We've added
rules to change how those are called/elided in the past knowing it could
break code. If you write a swap() its suppose to do swapping, if it does
something else you get what you deserve. "Optimizing" std::swap() to call
your types custom swap if it has one does not sound like a bad thing to me.
The fact that essential library primitives like swap() and begin()
require expert level knowledge to use correctly is a complete and utter
failure.* Really, its just ****ing stupid.* ADL is not the proper tool
for this. I'd argue ADL is only really useful for operator overloading.
Unless you put swap(), begin(), etc.. to the global :: namespace, ADL will
never work. That's a non-starter too as third party libraries wanting to
define "customization points" such as begin() certainly cannot just put
stuff in the global namespace.
With have virtual functions for runtime dispatch, and this broken crap
interface for compile time dispatch. This is a serious bug that needs to be
fixed.
I don't disagree on the importance of customization points. I don't
disagree with the need to have them work.
That alone however *cannot* justify doing it in a backwards-incompatible
way.
It also wouldn't help users create similar interfaces that have similar
Post by Matthew Fioravante
Post by Nicol Bolas
behavior.
That's a secondary concern.
I strongly disagree. It's like saying that we need to have ranges in the
standard library, but it's a secondary concern to provide tools to help
people write standard library compatible ranges. What good is it to have
standard idioms if writing idiom-compatible constructs is so complex/arcane
that users can not reasonably be expected to define them on their own?
Writing a standard library compatible range should not be hard. Writing a
standard library compatible customization point should also not be hard.
This should be a primary concern of any solution to this problem. Once we
standardize the concept of "customization point", people will and should
want to do it in a way that is compatible with the standard library. If we
make that simple, then we allow them to do so.
If we make the idiom complex or arcane, all we do is create dozens of
headaches down the road.
So then add the tools to help create these things. I never said we
shouldn't. However when making engineering trade-offs in the interface, its
better to design something optimized for the common case (calling a
customization point) over the rare case (writing the default dispatch
driver for a customization point library).
Post by Nicol Bolas
Post by Matthew Fioravante
If I want to write foo::fooify() customization point in libfoo, I can
lookup a tutorial to get all of the dispatching correct. I only need to do
this once for all of the thousands if not millions of times my users will
just need to say foo::fooify(fooable_thing);. If I'm writing my own
customization points, I'm already an expert C++ developer.
That kind of thinking is what gets us `std::enable_if`. "If I want to do
some SFINAE-style stuff, then I'm already an expert C++ developer".
I don't agree with this comparison. Hacking overload resolution with
enable_if is something we do way more often than creating new customization
points.

The use case here is not "I want to do SFINAE stuff" (expert only
implementation specific gibberish), its "I want write a math function which
only operates on any kind of `real number' type" (basic core idea).

Before we go ahead and add language features, ugly less invasive things
like this are a good start to get experience. Everybody hates enable_if for
good reason but before concepts that's all we had to solve a real need. The
standard library 10 years from now is not going to be any worse off for the
existence of an old enable_if template now collecting dust unused in the
new conceptified regime.
Post by Nicol Bolas
Utter nonsense. By adding appropriate things to the language, we give
novices the power to do the things that experts do. We take arcane idioms
and bring them to the masses.
When we get concepts, you'll see the number of people using SFINAE
increase dramatically. Why? Because it makes it a real part of the
language, not some arcane hack. Users have needs that SFINAE could solve,
but they don't use it because it's needlessly difficult and
over-complicated. The same goes here. Creating a customization points
should not be something that requires being an expert C++ developer. Users
want to be able to do these things, but as of yet, there is no good, simple
solution for it.
Furthermore, we could also consider adding helper utilities for this in
Post by Matthew Fioravante
the standard library, even if they have to be macros.
Yes, don't bother with a nice, neat language feature. It's much better to
do this sort of stuff as a macro.
Ok so invent a new language feature then. I'd be plenty happy with that
too. I don't think you can do this with templates as they are currently
which is why a macro was suggested. A language feature would need to be
more general purpose. I'm not sure it would ever fly to create a core
language feature solely for the purpose of writing customization points.

I'm not at all married to using std::swap() to solve the problem. If
someone has a better solution I'll be the first one to jump on board.
Whatever the solution is, this using std::swap; swap(); nonsense has got to
go.

It also needs to be solved not just for swap(), but for all customization
points including begin(), end(), cbegin(), cend(), rbegin(), rend(),
size(), data(), etc.. As an added bonus, this should also mean we can all
stop doing the brainless and bug friendly work of writing cbegin(), cend(),
rbegin(), rend(), rbegin() const, rend() const, crbegin(), crend() for all
of our custom container classes. Instead just relying on the default
adapters generated by the std:: default versions adapting begin(), begin()
const, end(), and end() const.



Lets not also forget we have concepts on the way. Once concepts are out in
the wild this ADL problem is going to become a much bigger issue fast. Its
was one of the main use cases driving unified function call syntax. Do we
want to wait until the bug reports and complaints after concepts or should
we not try to attack this issue now?

If I want to make an Iterable concept, I'd like to just say that it
begin(x) and end(x) are valid expressions which return Iterator concepts.
Why the hell do I need to deal with these using declaration gymnastics? How
do I carefully add them without polluting the entire scope? I still haven't
review the concepts proposal in detail. Can I even put a using declaration
inside a concept expression?

For homework, try to implement the following from scratch. Do not cheat
with type_traits, concepts, or any other std:: machinery that already
solves the problem. Do not pollute the enclosing scope with using
declarations.

* Given a type T, write a noexcept() expression which is true if the swap()
expression for an object of type T would be noexcept.
* Given a type T, write a decltype() expression to get the type resulting
from a begin() expression called on an object of type T.
--
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/68381f91-4180-429c-8f52-de00bd725c60%40isocpp.org.
Vicente J. Botet Escriba
2017-05-06 13:58:04 UTC
Permalink
Post by Nicol Bolas
Because it would be a backwards-incompatible change.
C++ has broken backwards compatibility before. Right now if
you call std::swap() on your object, unless its in std you'll
get a default move based swap operation. Suppose that was all
of the sudden silently changed to actually call your type's
swap() operation if it has one. How bad would that be? How
much code would it break?
I consider swap() special in the same vein as move and copy.
We've added rules to change how those are called/elided in the
past knowing it could break code. If you write a swap() its
suppose to do swapping, if it does something else you get what
you deserve. "Optimizing" std::swap() to call your types
custom swap if it has one does not sound like a bad thing to me.
The fact that essential library primitives like swap() and
begin() require expert level knowledge to use correctly is a
complete and utter failure.*Really, its just ****ing stupid.*
ADL is not the proper tool for this. I'd argue ADL is only
really useful for operator overloading. Unless you put swap(),
begin(), etc.. to the global :: namespace, ADL will never
work. That's a non-starter too as third party libraries
wanting to define "customization points" such as begin()
certainly cannot just put stuff in the global namespace.
With have virtual functions for runtime dispatch, and this
broken crap interface for compile time dispatch. This is a
serious bug that needs to be fixed.
I don't disagree on the importance of customization points. I
don't disagree with the need to have them work.
That alone however /cannot/ justify doing it in a
backwards-incompatible way.
It also wouldn't help users create similar interfaces that
have similar behavior.
That's a secondary concern.
I strongly disagree. It's like saying that we need to have ranges
in the standard library, but it's a secondary concern to provide
tools to help people write standard library compatible ranges.
What good is it to have standard idioms if writing
idiom-compatible constructs is so complex/arcane that users can
not reasonably be expected to define them on their own?
Writing a standard library compatible range should not be hard.
Writing a standard library compatible customization point should
also not be hard. This should be a primary concern of any solution
to this problem. Once we standardize the concept of "customization
point", people will and should want to do it in a way that is
compatible with the standard library. If we make that simple, then
we allow them to do so.
If we make the idiom complex or arcane, all we do is create dozens
of headaches down the road.
So then add the tools to help create these things. I never said we
shouldn't. However when making engineering trade-offs in the
interface, its better to design something optimized for the common
case (calling a customization point) over the rare case (writing the
default dispatch driver for a customization point library).
I agree that calling the customization must be as simple as possible.
However customizing shouldn't be too complex neither. These are simple
things.
Post by Nicol Bolas
If I want to write foo::fooify() customization point in
libfoo, I can lookup a tutorial to get all of the dispatching
correct. I only need to do this once for all of the thousands
if not millions of times my users will just need to say
foo::fooify(fooable_thing);. If I'm writing my own
customization points, I'm already an expert C++ developer.
That kind of thinking is what gets us `std::enable_if`. "If I want
to do some SFINAE-style stuff, then I'm already an expert C++
developer".
I don't agree with this comparison. Hacking overload resolution with
enable_if is something we do way more often than creating new
customization points.
The use case here is not "I want to do SFINAE stuff" (expert only
implementation specific gibberish), its "I want write a math function
which only operates on any kind of `real number' type" (basic core idea).
Right. We want to associate a function to a RealNumber type. We need the
minimal set of functions that define this RealNumber concept. I call
those customization points. We could as well define algorithms that have
a default implementation that could become customization points as the
user can do better. So we need customization points with default
behavior. Maybe we have a very good implementation for some more
constrained RealNumber, and I would expect we are able to customize once
for all to all the types that satisfy the additional constraints.
Post by Nicol Bolas
Before we go ahead and add language features, ugly less invasive
things like this are a good start to get experience. Everybody hates
enable_if for good reason but before concepts that's all we had to
solve a real need. The standard library 10 years from now is not going
to be any worse off for the existence of an old enable_if template now
collecting dust unused in the new conceptified regime.
Utter nonsense. By adding appropriate things to the language, we
give novices the power to do the things that experts do. We take
arcane idioms and bring them to the masses.
When we get concepts, you'll see the number of people using SFINAE
increase dramatically. Why? Because it makes it a real part of the
language, not some arcane hack. Users have needs that SFINAE could
solve, but they don't use it because it's needlessly difficult and
over-complicated. The same goes here. Creating a customization
points should not be something that requires being an expert C++
developer. Users want to be able to do these things, but as of
yet, there is no good, simple solution for it.
Furthermore, we could also consider adding helper utilities
for this in the standard library, even if they have to be macros.
Yes, don't bother with a nice, neat language feature. It's much
better to do this sort of stuff as a macro.
Ok so invent a new language feature then. I'd be plenty happy with
that too.
It seems it is no so simple :)
Post by Nicol Bolas
I don't think you can do this with templates as they are currently
which is why a macro was suggested. A language feature would need to
be more general purpose. I'm not sure it would ever fly to create a
core language feature solely for the purpose of writing customization
points.
I'm not at all married to using std::swap() to solve the problem. If
someone has a better solution I'll be the first one to jump on board.
Whatever the solution is, this using std::swap; swap(); nonsense has
got to go.
STD2 with Range TS, goes already in this direction. std2::swap will do
what you want. We are lucky we will use a new namespace.
Post by Nicol Bolas
It also needs to be solved not just for swap(), but for all
customization points including begin(), end(), cbegin(), cend(),
rbegin(), rend(), size(), data(), etc..
This is also the case for these functions.
Post by Nicol Bolas
As an added bonus, this should also mean we can all stop doing the
brainless and bug friendly work of writing cbegin(), cend(), rbegin(),
rend(), rbegin() const, rend() const, crbegin(), crend() for all of
our custom container classes. Instead just relying on the default
adapters generated by the std:: default versions adapting begin(),
begin() const, end(), and end() const.
Yes, see above about defaulted customization points.
Post by Nicol Bolas
Lets not also forget we have concepts on the way. Once concepts are
out in the wild this ADL problem is going to become a much bigger
issue fast. Its was one of the main use cases driving unified function
call syntax. Do we want to wait until the bug reports and complaints
after concepts or should we not try to attack this issue now?
I believe the problem should be solved asap. The main problem will be on
finding a consensual solution.
Post by Nicol Bolas
If I want to make an Iterable concept, I'd like to just say that it
begin(x) and end(x) are valid expressions which return Iterator
concepts. Why the hell do I need to deal with these using declaration
gymnastics? How do I carefully add them without polluting the entire
scope? I still haven't review the concepts proposal in detail. Can I
even put a using declaration inside a concept expression?
This makes customization points pseudo-keywords. We have namespace to
localize names. Why not start using them?

Vicente
--
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/ca0803ca-1f88-4f88-5f39-77602741b758%40wanadoo.fr.
Howard Hinnant
2017-05-06 01:48:33 UTC
Permalink
Right now if you call std::swap() on your object, unless its in std you'll get a default move based swap operation. Suppose that was all of the sudden silently changed to actually call your type's swap() operation if it has one. How bad would that be? How much code would it break?
I’ve seen this code in the wild:

struct A
{
void swap(A& a)
{
std::swap(*this, a);
}
};

I’m not claiming it is good code. Just that it exists. How frequent, I’m not sure. But more than none.

If we have std::swap call A’s swap, this A silently gets infinite recursion.

Howard
--
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/E859BC5E-2BFA-47BF-9B0E-45D95E380838%40gmail.com.
Mathias Gaunard
2017-05-05 18:33:19 UTC
Permalink
Post by Matthew Fioravante
The swap(), begin(), end(), size(), etc... is an embarrassment.
- Its ugly to adding these using declarations.
- Its crazy error prone. Too easy to forget a using declaration.
- These functions cannot be called in single expression contexts like
decltype().
Why can't we just make std::swap() do all of the magic dispatching
internally? Then the convention is people always call std::swap(),
std::begin(), etc..
One man's embarassment is another man's good design.
ADL clearly expresses it is an extension point.

Having a function in std:: calling things in your namespace isn't as clean
as looking up the function in the associated namespaces.
--
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/CALnjya9r3EupBRoVqY4b1Utbu7JEP%2BcwKqoT8dTZ8PLfimcuuA%40mail.gmail.com.
Nicol Bolas
2017-05-05 19:49:30 UTC
Permalink
Post by Mathias Gaunard
Post by Matthew Fioravante
The swap(), begin(), end(), size(), etc... is an embarrassment.
- Its ugly to adding these using declarations.
- Its crazy error prone. Too easy to forget a using declaration.
- These functions cannot be called in single expression contexts like
decltype().
Why can't we just make std::swap() do all of the magic dispatching
internally? Then the convention is people always call std::swap(),
std::begin(), etc..
One man's embarassment is another man's good design.
ADL clearly expresses it is an extension point.
Except for things that can't be ADL'd, of course (ie: all fundamental
types). And for types which use a member function that has the desired
behavior, rather than implementing it in two places. Which is why `using
std::swap` is used before attempting to invoke ADL swap.

Having to do that for every ADL customization point is the problem.
Post by Mathias Gaunard
Having a function in std:: calling things in your namespace isn't as clean
as looking up the function in the associated namespaces.
The idea is that `std::swap` would call the `swap` in your namespace via an
ADL call. But as a specific overload of the non-ADL `std::swap` function.
--
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/24714caa-e72b-452a-a12b-d9a94284cfbd%40isocpp.org.
Marc Mutz
2017-05-11 09:57:42 UTC
Permalink
Post by Nicol Bolas
Post by Mathias Gaunard
Having a function in std:: calling things in your namespace isn't as
clean as looking up the function in the associated namespaces.
The idea is that `std::swap` would call the `swap` in your namespace via
an ADL call. But as a specific overload of the non-ADL
`std::swap` function.
The idea is to allow users to overload std::swap, in namespace std (where
'swap' stands for every std function), provided the arguments mention user-
defined types.

As I wrote below in this thread, every std template is already a customisation
point. And used as such ever since Effective C++ came out, and that means
probably even earlier).

The embarrassement is not that I cannot specialise std::swap for my own types
(I can), the embarrassment is that I can't do it for my own class _templates_
(because of no partial function specialisation, this would need to overload
and this is forbidden).

I'd be fine if C++20 allowed to overload functions in namespace std, since as
soon as that is voted in, the present UB effectively becomes allowed (since no
implementation checks for it, anyway), and I can move my ADL swaps into
namespace std. This will not break users of the using-swap idiom. It will
break users that use unqualified swap() without using std::swap, but those
deserve to be broken.

Thanks,
Marc
--
Marc Mutz <***@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt, C++ and OpenGL Experts
Vicente J. Botet Escriba
2017-05-06 13:14:21 UTC
Permalink
Post by Matthew Fioravante
The swap(), begin(), end(), size(), etc... is an embarrassment.
- Its ugly to adding these using declarations.
- Its crazy error prone. Too easy to forget a using declaration.
- These functions cannot be called in single expression contexts like
decltype().
Why can't we just make std::swap() do all of the magic dispatching
internally? Then the convention is people always call std::swap(),
std::begin(), etc..
Would you agree to call them giving the specific concepts we are
customizing as e.g.

std::swappable::swap
std::range::begin
std::range::end
....

?

The new interface should, of course, ensure that it calls your
customization point.

This avoids the cyclic issue HH reported.

Vicente
--
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/657cfbb3-241e-e84e-5d46-01ef63526873%40wanadoo.fr.
o***@join.cc
2017-05-03 08:13:49 UTC
Permalink
Post by Arthur O'Dwyer
IMHO, the most elegant solution (basically Eric Niebler's proposal) would
involve MASSIVE breakage of old code and is thus suited only for "std2".
Wouldn't it work as std::swap2?
--
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/b125c1ef-c2aa-412b-8352-26039d3f7970%40isocpp.org.
Marc Mutz
2017-05-03 09:14:41 UTC
Permalink
Post by o***@join.cc
Post by Arthur O'Dwyer
IMHO, the most elegant solution (basically Eric Niebler's proposal) would
involve MASSIVE breakage of old code and is thus suited only for "std2".
Wouldn't it work as std::swap2?
Sorry for highjacking Olaf's reply. This is not about Olaf's answer.

While it's true that std::swap is the most-prominent example, it's also one
which really is trivial in many ways. Few are the types which these days of
move semantics _need_ a custom swap(). Unless you still need to support C++98
compilers, it's merely a questionable optimisation technique (the best you can
do it member-wise swapping, which apart from a little less memory used, is
hardly orders of magnitude faster than what the triple-move default
implementation does).

No, forget swap. The Swap Problem is not (just) about swap. Much more
interesting than swap() are those functions which are API enablers:

A class template representing angles _needs_ a sin() overload, and up pops the
question how to provide it: via ADL, even though most people will likely not
write

using std::sin;
return sin(a);

? Or as a specialisation of std::sin() (doen't work) or as an overload of
std::sin() (not allowed). Example: https://codereview.qt-project.org/191717
So, yeah, via ADL, and thus continues the embarrassment...


A non-std container should provide a version of LF v2's erase_if(). Even
though erase_if is only overloaded on types in namespace std, since it's
currently in namespace std::experimental, you still can't say

erase_if(c, p);

ie. rely on ADL, but need to have another using declaration:

using std::experimental::erase_if;
erase_if(c, p);

This stuff usually gets a laugh from non-C++ experts already.
But note how this using declaration will fail to compile when LF v2 is not
supported, requiring adding preprocessor magic around it:

#if defined(_cpp_lib_dunno_exact_name) && \
__cpp_lib_dunno_exact_name >= 201411
using std::experimental::erase_if;
#endif
erase_if(c, p);

Ok, all of this will be solved when erase_if moves into namespace std proper,
making this a bad example.

Let's look at STL algorithms:

A wonky container like QList can actually implement reversing as an out-of-
line function: https://codereview.qt-project.org/144071 It's not far-fetched
to want to specialise std::reverse() to call the member function, which would
require overloading std::reverse(). Or providing an ADL version, which,
however, requires every use of std::reverse() to say

using std::reverse;
reverse(b, e);

Here, we finally reached a case where no-one in the world writes code like
this.

As you can see, the issue is not at all restricted to swap(), begin(), size(),
data() and the very few other customisation points that are being discussed.
Do you seriously want to make a std::sin2, std::reverse2, ... for every single
function in namespace std?

So I wonder: What's the rationale for not allowing adding overloads under the
same conditions as allowing partial specialisations of class templates? If a
user messes up, how is it a worse mess with overloads than with class template
specialisations? What's so scary about it that you'd rather discuss _new
language features_ to enable customisation than to allow it by what C++
provides for ages, and even novices quickly grasp: overloads?

Thanks,
Marc
--
Marc Mutz <***@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt, C++ and OpenGL Experts
Arthur O'Dwyer
2017-05-06 03:07:33 UTC
Permalink
Post by Marc Mutz
While it's true that std::swap is the most-prominent example, it's also one
which really is trivial in many ways. [...] Much more
A class template representing angles _needs_ a sin() overload, and up pops the
question how to provide it: via ADL, even though most people will likely not
write
using std::sin;
return sin(a);
? Or as a specialisation of std::sin() (doen't work) or as an overload of
std::sin() (not allowed). Example: https://codereview.qt-project.
org/191717
So, yeah, via ADL, and thus continues the embarrassment...
A non-std container should provide a version of LF v2's erase_if(). Even
though erase_if is only overloaded on types in namespace std, since it's
currently in namespace std::experimental, you still can't say
erase_if(c, p);
using std::experimental::erase_if;
erase_if(c, p);
This stuff usually gets a laugh from non-C++ experts already.
But note how this using declaration will fail to compile when LF v2 is not
#if defined(_cpp_lib_dunno_exact_name) && \
__cpp_lib_dunno_exact_name >= 201411
using std::experimental::erase_if;
#endif
erase_if(c, p);
Ok, all of this will be solved when erase_if moves into namespace std proper,
making this a bad example.
A wonky container like QList can actually implement reversing as an out-of-
line function: https://codereview.qt-project.org/144071 It's not far-fetched
to want to specialise std::reverse() to call the member function, which would
require overloading std::reverse(). Or providing an ADL version, which,
however, requires every use of std::reverse() to say
using std::reverse;
reverse(b, e);
Here, we finally reached a case where no-one in the world writes code like
this.
As you can see, the issue is not at all restricted to swap(), begin(), size(),
data() and the very few other customisation points that are being discussed.
This is a very thoughtful description of a real problem that I had not been
considering, and I'm sorry it seems to have gotten minorly buried in this
thread.

I like your examples of std::sin() and std::reverse() as "customization
points." You're correct that I would not propose trying to use
Niebler-style "customization point" lambdas for those. One takeaway point
here is that the set of possible customization points in C++ is an open
set, not a closed set.

The std::reverse example is particularly interesting to me. Consider this
implementation of std::reverse, and then this implementation of std::rotate:

template<class It>
void reverse(It first, It last)
{
while (first != last) {
--last;
if (first == last) break;
using std::swap;
swap(*first, *last);
++first;
}
}

template<class It>
It rotate(It a, It mid, It b)
{
auto result = a + (b - mid);
std::reverse(a, b);
std::reverse(a, result);
std::reverse(result, b);
return result;
}

The implementation of std::reverse goes out of its way to respect the
"customization-point-ness" of swap, but the implementation of std::rotate
does *not* go out of its way to respect the "customization-point-ness" of
reverse. So, if your particular data type has a speedy reverse,
std::rotate will be inefficient.

Now, the Standard might reasonably say that this state of affairs is fine.
std::rotate isn't supposed to delegate to your speedy reverse; it's not
even guaranteed to call std::reverse (and in fact libstdc++ calls something
named __reverse, and libc++ doesn't use swap at all
<https://wandbox.org/permlink/59FAnIX3rWsY5xuw> in most cases). It's just
supposed to rotate the given range by some unspecified means, and if you
want a rotate function that *will* use your fast reverse, you'll just have
to write it yourself. This argument is fine, as far as it goes.

But the Standard Library isn't all libraries. IMHO it would be reasonable
for some library designer out there to say, "My numerics library is generic
enough to work with all numeric datatypes; if it ever calls sin(), or log(),
or max(), or sort(), or any function, it'll do it in a way that you can
customize." And then that designer will need a solution for how to call *any
arbitrary function* while respecting ADL. C++03 has a solution for that,
but it's clunky, and as Matthew Fioravante pointed out, the C++03 solution
doesn't play well with decltype(swap(a,b)) or noexcept(noexcept(swap(a,b))).

That is, one might say that the chief failing of the C++03 solution "using
std::swap; swap(a,b)" is that it has failed to keep up with the syntactic
changes in C++11. If it weren't for that, it would still be *okay*, if
never excellent.

my $.02,
–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/CADvuK0K7_xwz_zB%2Bo7NaB-TWjadEFqiUKOihigGZy_DOxg7BgQ%40mail.gmail.com.
Matthew Fioravante
2017-05-06 06:52:20 UTC
Permalink
One other deficiency of the using std::swap; swap(); idiom is that you lose
namespace scoping when invoking your customization points.


If I had liba::foo, and libb::foo separate customization points, I cannot
use them together in the same block of code without some very clumsy
scoping. Error prone and terribly ugly more complex code.

When I actually call it, I just say swap(x, y). I have an implicit
dependency on which swap was pulled in by a preceding using declaration.
Most of the time its on the preceding line, so its obvious. However if you
have a bunch of calls in the code, you'll want to organize the using
declarations and now thats one more thing to track when reading the code.


One compromise would be something like using namespace std::literals;

You might say something like:

using namespace std::custom;

Other libraries like libfoo can support using namespace foo::custom;

Then you automatically get swap(), and the others in your global namespace.
This makes ADL work at the expense of losing the capability of namespace
scoping in invoking customization point names. Fundamentally, this is the
only way ADL can work. You also still don't get some of the other examples
like reverse() or sin(). It all comes at a cost of this line of boilerplate
at the top of your source files and the decision that its always ok to have
these std symbols in your namespace.

I don't believe ADL at the callsite is up to the task. We either wrap its
deficiencies away with a standard name like std::swap, adl::swap, or
something else. Or somehow design yet another set of core language function
calling rules to somehow handle this case. The second option sounds
terrifying, especially with all the complicated rules the language already
has.

Unified call syntax was shot down. Right or wrong, it actually didn't solve
this problem either. UCS lets me not have to worry about saying swap(x, y)
vs x.swap(y). It still doesn't give me an automatic fallback to std::swap()
without a using declaration.

std::swap might be particularly scary. Howard Hinnant produced a great
counter example.

Another way out is to accept one of these swap operator proposals. Then we
can do it right with the swap operator from scratch. We completely sidestep
the swap() backwards compatibility issue and the ADL customization point
problem now only is limited to begin(), data(), size(), etc.. These
functions are an order of magnitude less dangerous than swap when it comes
to possibly breaking backwards compatibility.
Post by Arthur O'Dwyer
Post by Marc Mutz
While it's true that std::swap is the most-prominent example, it's also one
which really is trivial in many ways. [...] Much more
A class template representing angles _needs_ a sin() overload, and up pops the
question how to provide it: via ADL, even though most people will likely not
write
using std::sin;
return sin(a);
? Or as a specialisation of std::sin() (doen't work) or as an overload of
https://codereview.qt-project.org/191717
So, yeah, via ADL, and thus continues the embarrassment...
A non-std container should provide a version of LF v2's erase_if(). Even
though erase_if is only overloaded on types in namespace std, since it's
currently in namespace std::experimental, you still can't say
erase_if(c, p);
using std::experimental::erase_if;
erase_if(c, p);
This stuff usually gets a laugh from non-C++ experts already.
But note how this using declaration will fail to compile when LF v2 is not
#if defined(_cpp_lib_dunno_exact_name) && \
__cpp_lib_dunno_exact_name >= 201411
using std::experimental::erase_if;
#endif
erase_if(c, p);
Ok, all of this will be solved when erase_if moves into namespace std proper,
making this a bad example.
A wonky container like QList can actually implement reversing as an out-of-
line function: https://codereview.qt-project.org/144071 It's not far-fetched
to want to specialise std::reverse() to call the member function, which would
require overloading std::reverse(). Or providing an ADL version, which,
however, requires every use of std::reverse() to say
using std::reverse;
reverse(b, e);
Here, we finally reached a case where no-one in the world writes code like
this.
As you can see, the issue is not at all restricted to swap(), begin(), size(),
data() and the very few other customisation points that are being discussed.
This is a very thoughtful description of a real problem that I had not
been considering, and I'm sorry it seems to have gotten minorly buried in
this thread.
I like your examples of std::sin() and std::reverse() as "customization
points." You're correct that I would not propose trying to use
Niebler-style "customization point" lambdas for those. One takeaway point
here is that the set of possible customization points in C++ is an open
set, not a closed set.
The std::reverse example is particularly interesting to me. Consider this
template<class It>
void reverse(It first, It last)
{
while (first != last) {
--last;
if (first == last) break;
using std::swap;
swap(*first, *last);
++first;
}
}
template<class It>
It rotate(It a, It mid, It b)
{
auto result = a + (b - mid);
std::reverse(a, b);
std::reverse(a, result);
std::reverse(result, b);
return result;
}
The implementation of std::reverse goes out of its way to respect the
"customization-point-ness" of swap, but the implementation of std::rotate
does *not* go out of its way to respect the "customization-point-ness" of
reverse. So, if your particular data type has a speedy reverse,
std::rotate will be inefficient.
Now, the Standard might reasonably say that this state of affairs is fine.
std::rotate isn't supposed to delegate to your speedy reverse; it's not
even guaranteed to call std::reverse (and in fact libstdc++ calls something
named __reverse, and libc++ doesn't use swap at all
<https://wandbox.org/permlink/59FAnIX3rWsY5xuw> in most cases). It's
just supposed to rotate the given range by some unspecified means, and if
you want a rotate function that *will* use your fast reverse, you'll just
have to write it yourself. This argument is fine, as far as it goes.
But the Standard Library isn't all libraries. IMHO it would be reasonable
for some library designer out there to say, "My numerics library is generic
enough to work with all numeric datatypes; if it ever calls sin(), or
log(), or max(), or sort(), or any function, it'll do it in a way that
you can customize." And then that designer will need a solution for how to
call *any arbitrary function* while respecting ADL. C++03 has a solution
for that, but it's clunky, and as Matthew Fioravante pointed out, the C++03
solution doesn't play well with decltype(swap(a,b)) or
noexcept(noexcept(swap(a,b))).
That is, one might say that the chief failing of the C++03 solution "using
std::swap; swap(a,b)" is that it has failed to keep up with the syntactic
changes in C++11. If it weren't for that, it would still be *okay*, if
never excellent.
my $.02,
–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/6d8b6f86-a966-4450-9f9a-373e0a156963%40isocpp.org.
Vicente J. Botet Escriba
2017-05-06 13:41:53 UTC
Permalink
Post by Marc Mutz
Post by o***@join.cc
Post by Arthur O'Dwyer
IMHO, the most elegant solution (basically Eric Niebler's proposal) would
involve MASSIVE breakage of old code and is thus suited only for "std2".
Wouldn't it work as std::swap2?
Sorry for highjacking Olaf's reply. This is not about Olaf's answer.
While it's true that std::swap is the most-prominent example, it's also one
which really is trivial in many ways. Few are the types which these days of
move semantics _need_ a custom swap(). Unless you still need to support C++98
compilers, it's merely a questionable optimisation technique (the best you can
do it member-wise swapping, which apart from a little less memory used, is
hardly orders of magnitude faster than what the triple-move default
implementation does).
No, forget swap. The Swap Problem is not (just) about swap. Much more
Agreed.
Post by Marc Mutz
A class template representing angles _needs_ a sin() overload, and up pops the
question how to provide it: via ADL, even though most people will likely not
write
using std::sin;
return sin(a);
? Or as a specialisation of std::sin() (doen't work) or as an overload of
std::sin() (not allowed). Example: https://codereview.qt-project.org/191717
So, yeah, via ADL, and thus continues the embarrassment...
A non-std container should provide a version of LF v2's erase_if(). Even
though erase_if is only overloaded on types in namespace std, since it's
currently in namespace std::experimental, you still can't say
erase_if(c, p);
using std::experimental::erase_if;
erase_if(c, p);
Would you accept to call

std::container::erase_if(c,p);

or something like that?
Post by Marc Mutz
This stuff usually gets a laugh from non-C++ experts already.
But note how this using declaration will fail to compile when LF v2 is not
#if defined(_cpp_lib_dunno_exact_name) && \
__cpp_lib_dunno_exact_name >= 201411
using std::experimental::erase_if;
#endif
erase_if(c, p);
This is another issue.
Post by Marc Mutz
Ok, all of this will be solved when erase_if moves into namespace std proper,
making this a bad example.
A wonky container like QList can actually implement reversing as an out-of-
line function: https://codereview.qt-project.org/144071 It's not far-fetched
to want to specialise std::reverse() to call the member function, which would
require overloading std::reverse(). Or providing an ADL version, which,
however, requires every use of std::reverse() to say
using std::reverse;
reverse(b, e);
AFAIK, reverse is not a customization point. We will need a proposal for
that ;-)
Post by Marc Mutz
Here, we finally reached a case where no-one in the world writes code like
this.
As you can see, the issue is not at all restricted to swap(), begin(), size(),
data() and the very few other customisation points that are being discussed.
Do you seriously want to make a std::sin2, std::reverse2, ... for every single
function in namespace std?
I would prefer to associate them to the concept the customization is
associated to.
Post by Marc Mutz
So I wonder: What's the rationale for not allowing adding overloads under the
same conditions as allowing partial specialisations of class templates? If a
user messes up, how is it a worse mess with overloads than with class template
specialisations? What's so scary about it that you'd rather discuss _new
language features_ to enable customisation than to allow it by what C++
provides for ages, and even novices quickly grasp: overloads?
From Range TS customization point N4381 we have athe following goals

"The goals of customization point design are as follows (for some
hypothetical future customization point cust):

Code that calls cust either qualified as std::cust(a); or
unqualified as using std::cust; cust(a); should behave identically. In
particular, it should find any user-defined overloads in the argument’s
associated namespace(s).
Code that calls cust as using std::cust; cust(a); should not bypass
any constraints defined on std::cust.
Calls to the customization point should be optimally efficient by
any reasonably modern compiler.
The solution should not introduce any potential violations of the
one-definition rule or excessive executable size bloat."

I don't agree with all this. I don't believe that calling cust

using std::cust;
cust(a);

is a must work, even if this is the way we do now.


I will replace the goals by

Code that calls std::a_concept::cust(a); should end by calling the
user-defined customized point.
User can not call cust by ADL, that is using std::a_concept::cust;
cust(a) should be incorrect.
Calls to the customization point should be optimally efficient by
any reasonably modern compiler.
The solution should not introduce any potential violations of the
one-definition rule or excessive executable size bloat.


Of course we cannot prevent the user to call cust(a) directly, except if
we add some feature that prevents that ([N1691] Explicit Namespaces).


So to answer to your comment.we want a single point that is able to
ensure some constraints (Method Pattern) and call to the specific
customized part. Adding overload in std will not satisfy these goal. I
believe the goals are desirable.


Vicente
--
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/5080f7be-9182-a364-7d1f-8fdf9c9531cf%40wanadoo.fr.
Marc Mutz
2017-05-11 09:40:05 UTC
Permalink
Post by Vicente J. Botet Escriba
So to answer to your comment.we want a single point that is able to
ensure some constraints (Method Pattern) and call to the specific
customized part. Adding overload in std will not satisfy these goal. I
believe the goals are desirable.
You are already allowed to "customise" a) every std class template (by full or
partial specialisation, which needs to mention a user type), b) every std
function template (by full specialisation, which needs to mention a user
type). And there's no central dispatch to enforce constraints there, either
(and $DEITY forbid someone starts to specialise std::allocator, or
std::is_pod, ...).

I can already specialise std::reverse for any concrete instantiation of QList,
by full specialisation. The embarrassment is that I can't do this for the
QList template itself (ie. all instantations of it).

So, from my POV of developer-in-the-trenches all that talk about customisation
points, associcated with concepts, is just hot air. The issue is why overloads
of std function (s|templates), which are the moral equivalent of partial class
template specialisation, are not allowed, and why some complex years-in-the-
future mythical language feature is needed, to artifically constrain something
that's already unconstrained.

Thanks,
Marc
--
Marc Mutz <***@kdab.com> | Senior Software Engineer
KDAB (Deutschland) GmbH & Co.KG, a KDAB Group Company
Tel: +49-30-521325470
KDAB - The Qt, C++ and OpenGL Experts
Nicol Bolas
2017-05-11 16:06:41 UTC
Permalink
Post by Marc Mutz
Post by Vicente J. Botet Escriba
So to answer to your comment.we want a single point that is able to
ensure some constraints (Method Pattern) and call to the specific
customized part. Adding overload in std will not satisfy these goal. I
believe the goals are desirable.
You are already allowed to "customise" a) every std class template (by full or
partial specialisation, which needs to mention a user type), b) every std
function template (by full specialisation, which needs to mention a user
type). And there's no central dispatch to enforce constraints there, either
(and $DEITY forbid someone starts to specialise std::allocator, or
std::is_pod, ...).
Not everyone agrees that specialization should be allowed for function
templates either. P0551 (PDF)
<http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0551r0.pdf> makes
the case that this should be removed.

I can already specialise std::reverse for any concrete instantiation of
Post by Marc Mutz
QList,
by full specialisation. The embarrassment is that I can't do this for the
QList template itself (ie. all instantations of it).
So, from my POV of developer-in-the-trenches all that talk about customisation
points, associcated with concepts, is just hot air.
But that's not the general POV of "developer-in-the-trenches". That's the
POV of *your* development habits. Other developers don't necessarily do
that.

The issue is why overloads
Post by Marc Mutz
of std function (s|templates), which are the moral equivalent of partial class
template specialisation, are not allowed, and why some complex
years-in-the-
future mythical language feature is needed, to artifically constrain something
that's already unconstrained.
But you have to write these overloads. The whole point of customization
points is that it standardizes the interface *without* you being forced to
do something special. The `swap` customization point allows you to write
member `swap` or ADL `swap`, whichever is most appropriate for your type.
Your way requires writing member `swap` *and* overloading `std::swap`.

How is that an improvement? Oh sure, its easier on the *caller*, but it's
harder on the *writer*.

With a proper language feature, we can make it easier on everyone.
--
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/e919ad53-9ba7-4789-b514-e17fcdf04f54%40isocpp.org.
Vicente J. Botet Escriba
2017-05-11 20:02:36 UTC
Permalink
Post by Vicente J. Botet Escriba
Post by Vicente J. Botet Escriba
So to answer to your comment.we want a single point that is able to
ensure some constraints (Method Pattern) and call to the specific
customized part. Adding overload in std will not satisfy these
goal. I
Post by Vicente J. Botet Escriba
believe the goals are desirable.
You are already allowed to "customise" a) every std class template (by full or
partial specialisation, which needs to mention a user type), b) every std
function template (by full specialisation, which needs to mention a user
type). And there's no central dispatch to enforce constraints there, either
(and $DEITY forbid someone starts to specialise std::allocator, or
std::is_pod, ...).
Not everyone agrees that specialization should be allowed for function
templates either. P0551 (PDF)
<http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0551r0.pdf>
makes the case that this should be removed.
Thanks for pointing out this link. I missed it completely.

Vicente
--
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/a6bd5f65-0a97-2173-61a5-6dadad1bdc06%40wanadoo.fr.
Vicente J. Botet Escriba
2017-05-11 19:37:01 UTC
Permalink
Post by Marc Mutz
Post by Vicente J. Botet Escriba
So to answer to your comment.we want a single point that is able to
ensure some constraints (Method Pattern) and call to the specific
customized part. Adding overload in std will not satisfy these goal. I
believe the goals are desirable.
You are already allowed to "customise" a) every std class template (by full or
partial specialisation, which needs to mention a user type), b) every std
function template (by full specialisation, which needs to mention a user
type).
I was aware that we can specialize some classes, but not all, and even
less the full specialize a std function. Could you point me to the
references? Nevertheless this doesn't solve the customization points we
are talking of.
Post by Marc Mutz
And there's no central dispatch to enforce constraints there, either
(and $DEITY forbid someone starts to specialise std::allocator, or
std::is_pod, ...).
I can already specialise std::reverse for any concrete instantiation of QList,
by full specialisation. The embarrassment is that I can't do this for the
QList template itself (ie. all instantations of it).
I guess you are not happy then.
Post by Marc Mutz
So, from my POV of developer-in-the-trenches all that talk about customisation
points, associcated with concepts, is just hot air.
I will not talk about what developers think. I consider my self a
library developer and I don't agree with you here.
In generic programming we need customization points in one way or another.
Post by Marc Mutz
The issue is why overloads
of std function (s|templates), which are the moral equivalent of partial class
template specialisation, are not allowed, and why some complex years-in-the-
future mythical language feature is needed, to artifically constrain something
that's already unconstrained.
I believe someone has already responded to this question, but maybe it
was in another thread.

Vicente
--
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/f8add216-a4e1-9a47-9d06-3378d5d48e01%40wanadoo.fr.
Loading...