|
Boost : |
From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2002-11-20 13:38:17
----- Original Message -----
From: "Douglas Gregor" <gregod_at_[hidden]>
> On Tuesday 19 November 2002 04:44 pm, Paul Mensonides wrote:
> > Clever type traits are one thing, but they are a side-effect of the
> > solution to a more general problem. Specifically, one template function
> > declaration can permanently break an overload set--even if it is not
> > selected. The solution to this is obvious, if type deduction yields
> > nonsense, it should fail. It just so happens that we could exploit that
> > solution in significant ways with traits and expression validity
checking.
>
> I don't think it's quite so clear-cut, because we have to define what you
mean
> by "nonsense", and I'm not sure you'll find agreement. The committee chose
> one view of nonsense: trying to form any type that isn't a C++ type (e.g.,
> typename int::foo) is nonsense, so deduction fails.
This isn't what that section means however. It is only a list a few cases
of "trying to form any type that isn't a C++ type."
> But we're talking about a
> whole new dimension of nonsense, where we want it to say "anything that
can't
> compile is nonsense".
More or less, yes. Anything that is not semantically valid causes type
deduction failure.
> I have to wonder if this makes the problem you
> cite---that one function template declaration can permanently break an
> overload set---even worse. We're not talking about one brilliantly-written
> function template declaration, we're talking about one that is poorly
written
> because it is too general. It's an error that should be corrected, but the
> user won't ever actually see this error because the function template will
> never make it into the overload set.
I'm talking about the strict interpretation of the standard (14.8.3)--not
what current compilers do. Type deduction happens *before* overload
resolution--i.e. in the process or building a candidate set. The template
function just has to be visible. So, in order for the function to "never
make it into the overload set," it has to fail type deduction (i.e. not pass
and _not_ error).
template<unsigned> class x { };
template<class> class y { };
template<class T> void f( x<sizeof(T)>* );
template<class T> void f( y<T>* x, y<T>* y );
f<void>(0, 0) // error
struct z { };
template<class T> void g( T z::*, int );
template<class T> void g( int ... );
g<int&>(0, 0) // error
> Also note that this change is a huge problem for backward compatibility.
That
> badly broken function template from above is silently useless on C++0x,
but
> step back to C++98/C++02 and its going to fail.
This isn't a case of a "badly broken function template," nor is a case of it
being "silently useless." It can be a perfectly worthwhile function
template. All that has to happen is its declaration cannot be instantiated
with a certain template arguments, where doing so does not match one of the
listed criteria for type deduction failure in 14.8.2. Remember, this
happens *before* overload resolution, so it happens with every function
template that is in scope every time a call is made to a function with that
name. A given template function can *not* be selected in only two ways--1)
it fails type deduction which causes it to be invisible to overload
resolution, or 2) it passes type deduction but is not selected by regular
overload resolution.
As far as compatibility goes, this is a non-issue. What once was an error
would now be allowed (0x). That is not the same thing as something that
*was* allowed and later was an error. This change would do several things,
1) cleanup that section of the standard, 2) unify the rules with declaration
instantiation ala member functions, 3) give us a huge set of tools to work
with for generic programming, and 4) close a potentially serious hole in the
C++ overload mechanism. What this change *wouldn't* do is alter the results
on overload resolution in any way, nor would it allow more functions into
the candidate set. The *only* change would be that functions would be
removed automatically if declaration instantiation makes no sense. This is
just professional-quality design here, folks. As it is now, it is only
half-designed.
Furthermore, defining "nonsense" in the context which I use it is not
difficult. If type deduction yields a function declaration that would be
invalid if directly used in the source, then type deduction fails.
Obviously, it can be worded better than that, but you get the idea.
> > *why* something is invalid in the general sense. Also, the "sizeof"
trick
> > is open-ended. Any possible future language primitives will only give
us a
> > few of the things we need, and we'll still have to do the rest anyway.
> >
> > Paul Mensonides
>
> ... but don't forget that once we get more language primitives, that will
> enable new tricks as well.
Yes. My point is that whatever gets added to 0x is not going to solve all
of our needs. We should never throw away worthwhile tools--even if some of
them make us bend over backward to accomplish something (i.e. the sizeof
hack).
Paul Mensonides
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk