* 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?
Or, alternatively, can't
int i, j;
swap(i, j);
automagically find the std version?
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
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+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@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.
On Tue, May 2, 2017 at 6:09 PM, Nicol Bolas <jmck...@gmail.com> wrote:
On Tuesday, May 2, 2017 at 2:25:03 PM UTC-4, Marc Mutz wrote:
* 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);
[...]
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 "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.
(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)
On Tuesday, May 2, 2017 at 10:08:12 PM UTC-4, Arthur O'Dwyer wrote: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."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.
[...] 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 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).
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<>{})
Or we could just have lifting lambdas and deal with it that way. Whatever happened to P0119, anyway?
- 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.
On Tue, May 2, 2017 at 7:26 PM, Nicol Bolas <jmck...@gmail.com> wrote:On Tuesday, May 2, 2017 at 10:08:12 PM UTC-4, Arthur O'Dwyer wrote: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."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, 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).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?
On Tuesday, May 2, 2017 at 11:16:31 PM UTC-4, Arthur O'Dwyer wrote: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.
[...] 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.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.
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.
IMHO, the most elegant solution (basically Eric Niebler's proposal) would involve MASSIVE breakage of old code and is thus suited only for "std2".
On Tue, May 2, 2017 at 9:13 PM, Nicol Bolas <jmck...@gmail.com> wrote:On Tuesday, May 2, 2017 at 11:16:31 PM UTC-4, Arthur O'Dwyer wrote: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 madeauto [x,y] = t;equivalent toauto __t = t;auto&& x = get<0>(__t); // with compiler magic to make decltype(x) appear as a non-reference type, of courseauto&& 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.)
On Wednesday, May 3, 2017 at 1:17:34 AM UTC-4, Arthur O'Dwyer wrote:On Tue, May 2, 2017 at 9:13 PM, Nicol Bolas <jmck...@gmail.com> wrote:On Tuesday, May 2, 2017 at 11:16:31 PM UTC-4, Arthur O'Dwyer wrote: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 madeauto [x,y] = t;equivalent toauto __t = t;auto&& x = get<0>(__t); // with compiler magic to make decltype(x) appear as a non-reference type, of courseauto&& 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?
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?
Or we could just have lifting lambdas and deal with it that way. Whatever happened to P0119, anyway?
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).
Because it would be a backwards-incompatible change.
It also wouldn't help users create similar interfaces that have similar behavior.
On 5 May 2017 at 21:29, Matthew Fioravante <fmatth...@gmail.com> wrote:
>> 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.
> 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.
> 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.
> 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.
On 5 May 2017 at 18:54, Matthew Fioravante <fmatth...@gmail.com> wrote: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.
On Friday, May 5, 2017 at 1:03:10 PM UTC-5, Nicol Bolas wrote: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.
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.
On Friday, May 5, 2017 at 2:29:03 PM UTC-4, Matthew Fioravante wrote:On Friday, May 5, 2017 at 1:03:10 PM UTC-5, Nicol Bolas wrote: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.
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 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.
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
On Friday, May 5, 2017 at 2:58:13 PM UTC-5, Nicol Bolas wrote:On Friday, May 5, 2017 at 2:29:03 PM UTC-4, Matthew Fioravante wrote:On Friday, May 5, 2017 at 1:03:10 PM UTC-5, Nicol Bolas wrote: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).
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.
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.
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?
On Saturday 06 May 2017 15:41:53 Vicente J. Botet Escriba wrote:
> 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.
On Thursday, May 11, 2017 at 5:41:20 AM UTC-4, Marc Mutz wrote:On Saturday 06 May 2017 15:41:53 Vicente J. Botet Escriba wrote:
> 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) makes the case that this should be removed.