The current definition of INVOKE for member pointers to class (lets named it C) doesn't not allow to use as the first arguments types convertible to C as the first argument - the most common example would be the reference wrapper (see LWG Issue #2219). The solution proposed in the issue add supports only to the reference_wrapper, so user defined types with such conversions will still not work (ex. boost::reference_wrapper).
I would like to propose the different solutions that will cover support for classes converitble to C or references to C. The wording is a bit rough at this point, but I think is clearly stating the intent.
Viable reference types TR for member pointer p of type M T::* are:
- T&, T&& if M is not function type
- T cv&, T cv&& if M is function type without ref-qualification and with CV-qualification cv
- T cv ref if M is function type without ref-qualification ref and with CV-qualification cv
Define INVOKE (f, t1, t2, ..., tN) as follows:
— (static_cast<TR>(t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is convertible to TR where TR is viable reference type for f.
If the new definition will be accepted then, the behavior of the INVOKE will change for the classes that defines both operator* returning class C and having conversion operator to C, example:
struct weird
{
C& operator*();
operator C();
};
But for this cases the problem wont compile, so now silent behavior changes would be introduced.
Viable reference types TR for member pointer p of type M T::* are:
- T&, T&& if M is not function type
- T cv&, T cv&& if M is function type without ref-qualification and with CV-qualification cv
- T cv ref if M is function type without ref-qualification ref and with CV-qualification cv
The last one should say "with ref-qualification", not "without ref-qualification".
Define INVOKE (f, t1, t2, ..., tN) as follows:
— (static_cast<TR>(t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is convertible to TR where TR is viable reference type for f.
If the new definition will be accepted then, the behavior of the INVOKE will change for the classes that defines both operator* returning class C and having conversion operator to C, example:
struct weird
{
C& operator*();
operator C();
};
But for this cases the problem wont compile, so now silent behavior changes would be introduced.
I don't find such a class weird. Does the behavior change so that things that used to be valid are no longer
valid, then? Is this weird-struct ok for INVOKE under the current rules, but invalid under your proposal?
Define INVOKE (f, t1, t2, ..., tN) as follows:
— (static_cast<TR>(t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is convertible to TR where TR is viable reference type for f.In the case of downcast t1 (being a base class of T) wouldn't be convertible (implictly) to any of TR. The implementation do the same things.
For current wording this kind of classes will go trough the (*t).*f path. In my proposed implementation there will be dis-ambiguity between path using conversion operator and the path going via operator*, so it will be no longer valid. Also I find such weird, because they merge the pointer-to-C semantics with a obejct-of-type-C semantics, so in my opinion there is nothing wrong to make them ambiguous in cases of calling member function of C.
of preference for operator* being preferred to conversion functions. I also happen to think that having this
conversion support is worth breaking that kind of classes, because as I said, I don't find them weird. The
On 5 July 2013 01:33, <toma...@gmail.com> wrote:
Define INVOKE (f, t1, t2, ..., tN) as follows:
— (static_cast<TR>(t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is convertible to TR where TR is viable reference type for f.In the case of downcast t1 (being a base class of T) wouldn't be convertible (implictly) to any of TR. The implementation do the same things.Thanks. I am not well-versed with INVOKE, it would be helpful if it said somewhere that the conversion must be
implicit. Specifying it in terms of static_cast suggests otherwise, so perhaps that could be said directly
in the wording?
For current wording this kind of classes will go trough the (*t).*f path. In my proposed implementation there will be dis-ambiguity between path using conversion operator and the path going via operator*, so it will be no longer valid. Also I find such weird, because they merge the pointer-to-C semantics with a obejct-of-type-C semantics, so in my opinion there is nothing wrong to make them ambiguous in cases of calling member function of C.
Well, I think going through the (*t).*f path makes a lot of sense, compared to going via the conversion operator
which makes no sense to me, because it creates a temporary anyway. Having such a nonsense conversionbe ambiguous with the operator* thus doesn't make much sense to me either. In other words, I have a lot
of preference for operator* being preferred to conversion functions. I also happen to think that having thisconversion support is worth breaking that kind of classes, because as I said, I don't find them weird. The
standard may not include any such classes, but I have seen such code in the wild.
In the case of downcast t1 (being a base class of T) wouldn't be convertible (implictly) to any of TR. The implementation do the same things.Thanks. I am not well-versed with INVOKE, it would be helpful if it said somewhere that the conversion must be
implicit. Specifying it in terms of static_cast suggests otherwise, so perhaps that could be said directly
in the wording?
To be clear. That was my intent when I was making this wording and that is how my implementation works.
Well, I think going through the (*t).*f path makes a lot of sense, compared to going via the conversion operator
which makes no sense to me, because it creates a temporary anyway. Having such a nonsense conversionbe ambiguous with the operator* thus doesn't make much sense to me either. In other words, I have a lot
of preference for operator* being preferred to conversion functions. I also happen to think that having thisconversion support is worth breaking that kind of classes, because as I said, I don't find them weird. The
standard may not include any such classes, but I have seen such code in the wild.
Firstly, consider the following function:
struct weird2
{
C& operator*();
operator C&();
};
Is there any reason to prefer operator* over conversion operator? For my point of view there is not.
Secondly, consider the following code:
void free_standing(const C&);
free_standing(weird()); //ups, I forgot to invoke *
free_standing(weird2());
In this case the conversion operator will be invoked instead of operator*, so I my personal preference would be to preffer conversions over operator* (as will free-standing functions do). That difference in personal taste convince me that should cases
Indeed, it isn't. It isn't far from a wrapper class that turns an
integer into an iterator.
-- Gaby
Why would you need a conversion to int operator in addition to operator* that returns in int in such wrapper?
I recommend testing that the implementation actually does that, then, and changing the wording to say thatit really requires implicit convertibility.
No, but it does no harm, and gives a sane result. Doing the opposite does no harm either in this
case, but it does harm in the case of the original "struct weird". Prefering operator* seems sanein all cases. Perhaps that's why the current INVOKE does it.
INVOKE doesn't model just free-standing functions. It has to make some compromises to support the variety of things
it supports, one of those compromises being that it can't model free-standing functions exactly.
It is a wrapper around integer, shouldn't it have that conversion?
W dniu piątek, 5 lipca 2013 01:43:10 UTC+2 użytkownik Ville Voutilainen napisał:I recommend testing that the implementation actually does that, then, and changing the wording to say thatit really requires implicit convertibility.
It was tested against that case.
No, but it does no harm, and gives a sane result. Doing the opposite does no harm either in this
case, but it does harm in the case of the original "struct weird". Prefering operator* seems sanein all cases. Perhaps that's why the current INVOKE does it.I would harm in case of:
struct weird3
{
C operator*();
operator C&();
};
For my perspective it does. I see the mem_fn as converting the member function M (Class::*)(Args...) cv ref to be invokable as free standing-function M (Class cv ref, Args...) (if ref is missing there should be & and && versions), exactly the same as it used for doing an overload resolution. In addition it supports pointers to Class and pointer-like types.INVOKE doesn't model just free-standing functions. It has to make some compromises to support the variety of things
it supports, one of those compromises being that it can't model free-standing functions exactly.
For my perspective it does. I see the mem_fn as converting the member function M (Class::*)(Args...) cv ref to be invokable as free standing-function M (Class cv ref, Args...) (if ref is missing there should be & and && versions), exactly the same as it used for doing an overload resolution. In addition it supports pointers to Class and pointer-like types.I may have used imprecise terminology. INVOKE certainly unifies target types so that their invocations become
similar to free functions. However, that doesn't necessarily mean INVOKE should behave as if all its targets
were free functions, because they aren't.
Well, just because one doesn't see the point means it is worth breaking.
It would be devastating if one was to use lack of vision as license to
break stuff. Even though we can't forsee every development, we usually
try hard.
But I would like them, to model this functions as close as possible to the free standing functions, so I would rather see prefenrecen of conversion operator over operator*. But that is the case of the presonal taste, so I think that I would be the best to come out with the solution that will choose some default behavior for such classes (probably the going trought operator* as it is not breaking) but allow to change it, but at this moment I don't see a clean way to do it.
It's not just a matter of taste. The apparently current preference of operator* (even if as a result of not considering
conversions functions at all) is the status quo in a released standard. Breaking those semantics should not
be done lightly, even if some personal preference suggests otherwise.
2013/7/5 Jonathan Wakely <c...@kayari.org>:
> I'd like to see some more convincing motivation for this change.
> std::reference_wrapper is a good motivating case, but I'm less sure about
> boost::reference_wrapper ... do you really need another reference_wrapper if
> you have a C++11 implementation? (I'm aware that the boost one allows
> incomplete types.)
Making *any* changes to the INVOKE semantics should not be done lightly, it's seriously tricky stuff!
I'd like to see some more convincing motivation for this change. std::reference_wrapper is a good motivating case, but I'm less sure about boost::reference_wrapper ... do you really need another reference_wrapper if you have a C++11 implementation? (I'm aware that the boost one allows incomplete types.) Such wrapper types can always be made to work with INVOKE by providing operator* so the last bullet of INVOKE handles them, although that's a bit smelly, I admit.