Boost logo

Boost :

From: Paul Mensonides (pmenso57_at_[hidden])
Date: 2002-11-12 19:10:35


----- Original Message -----
From: "Douglas Gregor" <gregod_at_[hidden]>

> On Tuesday 12 November 2002 04:26 am, Aleksey Gurtovoy wrote:
> > Well, since that particular reason of type deduction failure (attempting
to
> > call a member function on the object which type does not contain the
> > specified member) is not explicitly listed under 14.8.2 para 2, yes, in
one
> > or another form. I haven't figured out yet what would be the best way
> > (politically and technically) to get that part of the language into the
> > state we would like it to have ;).
>
> Once we've solved the "technically", I think the politically best way to
> handle this would be to submit a very detailed defect report. The problem
is
> easy to point out, and it is easy to give a case where the standard is
> ambiguous. However, we must give the committee the "big picture" regarding
> their decision: resolving the defect "our way" will make C++ a vastly more
> powerful and more safe language, because we will have the power to do
> concept-checking without spewing mountains of errors when we find a
failure.
> And someone better be at the meeting when the committee discusses this :)

Prior to 4.3, Comeau C++ had some trouble with this sort of thing (internal
compiler errors, etc.). Unfortunately, they seem to have closed that gap
incorrectly. For example:

#include <iostream>

template<unsigned> helper { };

template<class T> char check(helper<sizeof(&T::f)>*);
template<class T> char (& check(...))[256];

struct X {
    void f(void) { };
};

int main(void) {
    std::cout << sizeof(check<X>(0)) << &std::endl;
    return 0;
}

Comeau chooses the variadic (...) overload, which is clearly wrong. It is
sidestepping the argument over that section of the standard altogether with
results like this.

Here are the main problems with type deduction failure that I see. The
standard mentions two separate types of function "instantiation"--normal
instantiation of a template function and instantiation of just the
declaration of a function (and no other types). For example, all member
function declarations of a template class are instantiated even if the
member function itself is never instantiated. Any semantic error that
occurs during this process causes the entire class instantiation to fail.
E.g....

template<class T> struct X {
    void mf(int X::*) {
        return;
    }
};

X<int> x; // error: void mf(int int::*) is invalid

The standard is unclear which type of instantiation is involved with type
deduction. Obviously not a full function instantiation, but a few people
have said it needs only to instantiate what it can to produce a candidate
for overload resolution--the standard makes no mention of this at all, and
this doesn't follow anyway. Take the following example:

// a template that has or doesn't have
// the internal typedef 'type'

template<unsigned> struct sample { };

template<> struct sample<sizeof(char)> {
    typedef int type;
};

// two overloads of 'check'

template<class U> int check(typename sample<sizeof(U::f())>::type*) {
    return 0;
}

template<class U> int check(...) {
    return 1;
}

// sample classes

struct X {
    static char f(void);
};

struct Y {
    static void f(void);
};

struct Z { };

int main(void) {
    std::cout
        << check<X>(0) << ' '
        << check<Y>(0) << ' '
        << check<Z>(0) << &std::endl;
    return 0;
}

Comeau C++ yields 1 for all three of these tests. Here is the root of the
problem. In order to tell whether or not the instantiation of 'sample' has
a member 'type', the compiler *must* evaluate the sizeof() argument. In the
case above, type deduction should succeed when the argument is 'X', yielding
a valid candidate that is better than the ellipsis (which should cause the
output of 0 for X). In the other two cases ('Y' and 'Z'), type deduction is
in no-man's land. There are only two options specified by the
standard--success or failure. Type deduction cannot possibly succeed, as
Y::f returns void which is an invalid argument to sizeof, and Z::f doesn't
exist. Likewise, type deduction cannot fail according to the list specified
for type deduction failure. The *only* other possibility is a compile-time
error, since there is *no* way that a valid candidate can be produced.
Herein lies the most major problem: a single template function declaration
can completely break an overload set.

Note: Template type deduction happens during the creation of a candidate
set *prior* to actual overload resolution. In other words, the compiler is
not technically supposed to bypass this order because it "knows" the result
of overload resolution already (i.e. by the arguments). It *must* do the
type deduction.

This is unacceptable. What really needs to happen, is the rules regarding
declaration instantiation of template class members need to be merged with
type-deduction failure. I.e. If the result is semantically invalid, the
function is removed from the overload set (excepting only a few things such
as applying 'const' to a type that is already 'const' and possibly the
reference to reference issue). Period. This is the only safe way to go,
and conveniently, would allow for all sorts of traits tests that currently
rely on compiler extensions.

Paul Mensonides


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk