Topic: 14.8.2 Clarification?
Author: richard@ex-parrot.com (Richard Smith)
Date: Thu, 28 Mar 2002 20:01:34 GMT Raw View
google@vandevoorde.com (Daveed Vandevoorde) wrote in message news:<52f2f9cd.0203261846.7f369bc0@posting.google.com>...
> "Richard Smith" <richard@ex-parrot.com> wrote:
> > "Daveed Vandevoorde" <google@vandevoorde.com> wrote in message
> > news:52f2f9cd.0203230800.19341dc4@posting.google.com...
> > > "Paul Mensonides" <pmenso57@attbi.com> wrote:
[ ... ]
> > > > Basically, it boils down to this: Is type deduction a three result
> (i.e.
> > > > succeed, fail, error) mechanism or is it a two result (i.e. succeed,
> fail)
> > > > mechanism?
> > >
> > > It's a binary condition, but what may at first look as a deduction
> > > failure is not, and can instead lead to an instantiation failure
> > > afterwards.
> >
> > I'm not sure I understand this. If in some cases deduction succeeds and
> > then instantiation fails, what happens? Is an error diagnostic emitted? If
> > an error isn't diagnosed, does the specialisation still get added to the
> > candidate function set?
>
> There are two kinds of "instantiation". The standard almost always
> talks about the kind of instantiation triggered by a point of instantiation
> (POI). However, the third bullet of 14.8.2/2 describes a substitution
> that is also referred to in 14.7.1/8 as "instantiating a declaration".
> The candidate is built using the latter kind of (partial) specialization.
> When selected, a POI is created and (full) instantiation occurs,
> including all the semantic constraints that apply at that point.
This is not unique to function overloading: when you instantiate a
class, you instantiate the declarations, but not the definitions, of
all of its members, and there errors are diagnosed.
> > If an error occurs and is immediately diagnosed, then the system is
> > effectively ternary: an error occuring is one possible outcome.
> >
> > If an error occurs but isn't diagnosed, and the function isn't added to the
> > candidate function set, then why bother having a list in 14.8.2/2? There is
> > no discernable difference between deduction failing and instantiation
> > failing.
> >
> > If an error occurs but isn't diagnosed, and the function is added to the
> > candidate function set, what was the point in instantiating the function?
>
> The goal is to create a candidate for overload resolution. If that
> candidate is selected you get an error.
So you're saying that my third point is effectively correct (although
perhaps the implementation doesn't know the error has occured)?
This seems contrary to what the standard actually says. Instantiation
of the declaration of the function for inclusion in the candidate
function set is no different from instantating a class member's
declaration, and the same things should occur. Are you saying that a
compiler needn't diagnose an error for the following
class X { X(int, int) {} };
template <size_t> helper {};
template <class T>
class foo {
public:
void bar( helper<sizeof(new T)>* ) {} // never called, so not
fully
// instantiated
};
int main() {
foo<X> tmp;
}
Or even for the declaration of the following uncalled, undefined
function:
class X { X(int, int) {} };
template <size_t> helper {};
void baz( helper<sizeof(new T)>* );
I think that if instantiating the declaration of the overload for
insertion in the candidate function set doesn't require a diagnostic
(which is, unless I'm misunderstanding you, your belief), then the
instantiation of the declaration of the member foo<X>::bar doesn't
*require* a diagnostic; and probably even the free function baz
doesn't (although this is on more tenuous ground).
[ ... ]
> > > > Can someone clarify this (at least the intent) for me?
> > >
> > > I remember when the bullet list of 14.8.2/2 was added and clearly the
> > > intent was to replace general (somewhat vague) phrasing by an exhaustive
> > > list of things where "substitution failure in not an error" (I call it
> > > the SFINAE principle in "C++ Templates"). As you note, it's useful for
> > > all kinds of classification tests. I suspect the list could be tweaked
> > > some more, but the intent is also to limit the amount of substitution
> > > that must be done for SFINAE cases.
> >
> > It seems perverse to want an exhaustive list of situations when type
> > deduction can fail. Surely it would be much simpler, and I imagine no
> > harder to implement, to simply ditch the list entirely, and only add those
> > functions whose declarations can be instantiated to the candidate function
> > list.
>
> I don't think it would be simpler or desirable. Ordinary instantiation
> is not desirable because that would make programs ill-formed just because
> we're trying to create a lesser candidate.
Yes, "ordinary" instantiation of the function *definition* clearly is
undesireable, but this is not what I suggested. I'm simply suggesting
that if the instantiation of the *declaration* (which the standard
requires) produces an ill-formed declaration, the declaration should
be dropped from the candidate function list. The standard currently
doesn't (AFAIK) explicitly indicate what should happen in this
circumstance.
> What 14.8.2 does is to define
> an alternative "partial" instantiation.
No. 14.8.2 references the idea of instantiation of declarations which
is used elsewhere in the standard. Unless I'm misremembering the
relevant passage (I've left my copy of the standard at work, so this
is from memory, and I could be wrong) there is no mention of "partial"
instantiation at all.
> and the exhaustive list avoids us
> having to define semantics (and from an implementation's point of vue:
> representations) for new (often nonsensical) types.
Well simply saying all declarations involving nonsensical type are
dropped from the candidate function list isn't too complicated -- IMO
it's simpler than having an exhaustive list.
> > Otherwise, I can see the list getting tweaked again and again as more
> > and more increasingly esoteric ways in which substitution can fail are
> > discovered.
>
> Unfortunately, that is a challenge.
Which is why I think a solution without an explicit list would be
preferable.
--
Richard Smith
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Richard Smith" <richard@ex-parrot.com>
Date: Wed, 27 Mar 2002 13:14:03 GMT Raw View
"Paul Mensonides" <pmenso57@attbi.com> wrote in message
news:8bQn8.107792$Yv2.32754@rwcrnsc54...
>
> There is an order of occurance here:
>
> I. explicit arguments are given to the template function
> II. type deduction occurs
> A. type deduction succeeds
> 1. function declaration is instantiated
> 2. function is added to candidate set
> B. type deduction fails
> 1. no instantiation of function is added to candidate set
> III. overload resolution occurs
> A. if selected, function definition is instantiated
>
> According to 14.8.3, *if* type deduction succeeds, '3a' would have to be
an
> error, which would break the entire candidate set. This order is
absolutely
> defined in the standard to be this way, whether or not that was the
intention.
I think it's more reasonable to argue that the error is diagnosed at II.A.1
when the declaration is instantiated to be added to CF set rather than
waiting until III.A when the definition is instantiated. After all, it is
the instantiation of the *declaration* not the definition that should
produces the error. There is nothing in the Standard to say that errors
that would normally require diagnostic can be ignored in II.A.1. There
really only two possibilities: either type deduction succeeds in which case
an error would seem to be mandatory, or type deduction fails in which case
the declaration is silently dropped from the candidate function set.
If type deduction can succeed, yet an error not be diagnosed, this would
implies that the instantiation of the function declaration can occur without
error, which would mean that a compliant implementation need not issue a
diagnostic for the following translation unit:
template<unsigned> class helper {};
class X { X(int, int) {} };
// Note: not overloaded or templated
void foo( helper< sizeof(new X) > * );
// No call to foo().
I hope we can agree that this example requires a diagnostic.
> > > If the list is exhaustive, then argument deduction should *succeed*,
at
> which
> > > point an instantiation of the first function template (at least the
> signature)
> > > should have been attempted (which of course, would error).
> >
> > Argument deduction succeeds and presumably results in a valid
> > parameter type "sample<some-value>" (which allows the "partial
> > instantiation" of 14.8.3). I understand 14.7.1/5 to say
> > that "sample<sizeof(new T)>" need not be instantiated if the
> > overload resolution process can determine the result without
> > that instantiation.
>
> 14.7.1/5 is about instantiations of 'class' template definitions, not
function
> template declarations.
All this is saying is that it is undefined whether the *definition* of the
class specialisation 'helper<
sizeof(new X) >' is instantiated if the overload resolution can be
determined without doing so. In Paul's first example it doesn't matter
whether helper is instantiated, indeed, if it weren't for this paragraph, I
would be very surprised if helper were instantiated. In my non-templated,
non-overloaded example above, 'helper< sizeof(new X) >' definately isn't
instantiated (it can even be an incomplete type), yet it still produces an
error.
> 14.7.1/8 says...
>
> "If a function template or a member function template specialization is
used in
> a way that involves overload resolution, a declaration of the
specialization is
> implicitly instantiated (14.8.3)."
>
> That doesn't leave much to the imagination.
>
> > I'm actually not quite sure whether a diagnostic is _allowed_
> > here; it seems it is from 14.7.1/5.
I agree that 14.7.1/5 *could* cause an error, but only in those
circumstances where the the instantiation of the declaration *must* cause an
error, so it is largely irrelevant.
--
Richard Smith
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Anthony Williams"<anthwil@nortelnetworks.com>
Date: Wed, 27 Mar 2002 13:30:48 GMT Raw View
"Daveed Vandevoorde" <google@vandevoorde.com> wrote in message
news:52f2f9cd.0203261817.68958174@posting.google.com...
> "Paul Mensonides" <pmenso57@attbi.com> wrote:
> > "Daveed Vandevoorde" <google@vandevoorde.com> wrote:
> > There is an order of occurance here:
> >
> > I. explicit arguments are given to the template function
> > II. type deduction occurs
> > A. type deduction succeeds
> > 1. function declaration is instantiated
> > 2. function is added to candidate set
> > B. type deduction fails
> > 1. no instantiation of function is added to candidate set
> > III. overload resolution occurs
> > A. if selected, function definition is instantiated
> > According to 14.8.3, *if* type deduction succeeds, '3a' would have to be
an
> > error, which would break the entire candidate set. This order is
absolutely
> > defined in the standard to be this way, whether or not that was the
> > intention.
> I don't think the issue is the order in which things occur. Instead,
> I believe the core issue is the notion of "instantiating a function
> declaration", which is part of the deduction process and described in
> the third bullet of 14.8.2/2. The goal of this "partial instantiation"
> is to create a candidate; it does not require the semantic checks
> associated with a normal instantiation.
struct X
{
X(int,int){}
};
template<unsigned>
struct S{};
template<typename T>
struct Y
{
void func(S<sizeof(new T)>);
};
Y<X> dummy;
The instantiation of a class template requires the instantiation of the
declaration of all its member functions. Therefore the instantiation of Y<X>
requires the instantiation of the declaration of Y<X>::func, which requires
that the sizeof expression be evaluated in order to determine which variety
of S<> it accepts as a parameter. However, "new X" is not a valid
expression, so the sizeof expression is invalid, and Y<X> is not a valid
instantiation of Y.
How is this "instantiation of the declaration" different from that required
by 14.8.2/2?
> > > > If the list is exhaustive, then argument deduction should *succeed*,
at
> > which
> > > > point an instantiation of the first function template (at least the
> > signature)
> > > > should have been attempted (which of course, would error).
> > 14.7.1/8 says...
> > "If a function template or a member function template specialization is
used in
> > a way that involves overload resolution, a declaration of the
specialization is
> > implicitly instantiated (14.8.3)."
> > That doesn't leave much to the imagination.
> I agree that the order of things is quite clear, but that doesn't
> mean that sizeof(new T) is an error during the substitution process;
> no invalid type is created here.
sample<sizeof(new T)> is an invalid type, as sizeof(new T) is invalid,
because "new T" is not a valid expression, since T doesn't have a default
constructor, where T==X.
> > > I'm actually not quite sure whether a diagnostic is _allowed_
> > > here; it seems it is from 14.7.1/5.
> > What about my other example?
> > template<unsigned> struct sample {
> > // no type
> > };
> > template<> struct sample<0U> {
> > struct type { };
> > };
> > template<class T> void check( typename sample<sizeof(new T) -
> > sizeof(T*)>::type* ) { }
> > template<class T> void check( int ) { }
> > struct X {
> > X(int, int) { } // no default ctor
> > };
> > int main(int argc, char* argv[]) {
> > check<X>(0);
> > return 0;
> > }
> > Is this supposed to be an error or not? The third sub-bullet of 14.8.2
says
> > that "Attempting to use a type in the qualifier portion of a qualified
name that
> > names a type when that type does not contain the specified member, or if
the
> > specified member is not a type where a type is required," results in
type
> > deduction failure.
> > The above example directly requires that 'sizeof(new T) - sizeof(T*)' be
> > evaluatated to determine whether the specialization of 'sample' contains
the
> > member 'type'.
> Indeed, but that doesn't imply that semantic constraints on "new T"
> must be checked (at least not until a POI (point of instantiation) is
> created).
Why not? How can sizeof(expression) be calculated without checking
expression for validity?
What about sizeof(T())? is that always the same as sizeof(T)? Even if this
particular T doesn't have a default constructor? What if we changed Paul's
example to sizeof(T())-sizeof(T) instead of sizeof(new T)-sizeof(T*)?
sizeof(new T) seems no different to me --- a new expression for type T
always returns a T*, but sometimes you have to specify constructor
parameters and sometimes you don't.
> > Yet this example still compiles. It never says that
> > type-deduction is optional if the result of overload resolution is
already
> > known. This example compiling does not follow the order layed out by
the
> > standard.
> I believe it does.
> > Type-deduction *then* overload resolution.
> OK.
> > Type deduction can either fail or succeed (or error?). In the above
example, type deduction should have
> > failed because of the lack of a member 'type', yet it cannot make the
evaluation
> > to determine that because 'X' has no default ctor.
> Not at all: deduction succeeds with T = X. This involves the substitution
> mentioned in the third bullet (not sub-bullet) of 14.8.2/2, which does not
> create an invalid type. The check that X has a default constructor is not
> mandated, so the partial instantiation (another term for "instantiation of
> a declaration") has no problem creating a candidate
check<>(sample<0>::type*).
> That candidate is not selected by overload resolution and therefore no POI
> is created for that candidate, and hence the semantic check that X has a
> default constructor is never done.
> > So what happens? Does type
> > deduction somehow succeed (when it shouldn't), or does it fail because
type
> > deduction found something that made no sense? As I've pointed out
several
> > times, this is supposed to happen *before* overload resolution. The
standard
> > does not adequately describe what should happen in a case such as this,
> > therefore since type-deduction cannot succeed (by the quoted bullet) yet
cannot
> > fail because it can't evaluate 'sizeof(new T)'. The only thing left is
> > compile-time error (or removal from the overload set, which would be the
logical
> > thing to do).
> See my analysis above.
> I do agree that you can squint at the standard to make it go either way,
> but I'm quite sure the intent is as explained above. The idea here is
> that adding an nonselected candidate template should not cause a program
> to become ill-formed. At the same time, that candidate must use
> representable types if it is to participate in overload resolution
> (hence the sub-bullet list in 14.8.2/2).
Surely that is the point --- if you add an extra overload that cannot be
instantiated (because T doesn't have a default constructor, or doesn't have
the correct nested type, etc.) then this shouldn't affect overload
resolution, as it cannot be selected.
Anthony
--
Anthony Williams
Software Engineer, Nortel Networks Optical Components Ltd
The opinions expressed in this message are not necessarily those of my
employer
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@attbi.com>
Date: Wed, 27 Mar 2002 18:21:27 GMT Raw View
"Daveed Vandevoorde" <google@vandevoorde.com> wrote in message
news:52f2f9cd.0203261817.68958174@posting.google.com...
[snip]
> > According to 14.8.3, *if* type deduction succeeds, '3a' would have to be an
> > error, which would break the entire candidate set. This order is absolutely
> > defined in the standard to be this way, whether or not that was the
> > intention.
>
> I don't think the issue is the order in which things occur. Instead,
> I believe the core issue is the notion of "instantiating a function
> declaration", which is part of the deduction process and described in
> the third bullet of 14.8.2/2. The goal of this "partial instantiation"
> is to create a candidate; it does not require the semantic checks
> associated with a normal instantiation.
Where is the list of semantic checks that it needs to perform? We have an
example in 14.7.1/1. When a class is implicitly instantiated, all of the member
function declarations are 'instantiated'. For instance,
template<class T> struct X {
T obj;
int& operator->*(int T::* ptr) {
return obj.*ptr;
}
};
int main() {
X<int> x; // compile-time error
return 0;
}
This similar example causes a compile-time error also...
struct X {
X(int, int) { }; // no default ctor
};
template<unsigned> struct tester { };
template<class T> struct Y {
void f(tester<sizeof(new T)>*) {
return;
}
};
int main() {
Y<X> y;
return 0;
}
Apparently the 'instantiation' of the declaration is an error, I don't see where
it says that the instantiation of a declaration here is any different than that
of 14.7.1/7 (with direct reference to 14.8.3).
[snip]
> > 14.7.1/5 is about instantiations of 'class' template definitions,
> > not function template declarations.
>
> Yes, I wrote nonsense there.
:)
> > 14.7.1/8 says...
> >
> > "If a function template or a member function template specialization is used
in
> > a way that involves overload resolution, a declaration of the specialization
is
> > implicitly instantiated (14.8.3)."
> >
> > That doesn't leave much to the imagination.
>
> I agree that the order of things is quite clear, but that doesn't
> mean that sizeof(new T) is an error during the substitution process;
> no invalid type is created here.
No 'invalid type' according to what definition? The bulleted list mentions
cases where deduction may fail, not the definition of an invalid type--which is
mentioned before the bulleted list. Also before the bulleted list are *other*
cases where type deduction my fail. That infers that 'invalid type' is a little
more opened ended than just the bulleted list. I don't disagree with you on the
intent. You were there; I was not. :)
[snip]
> > Is this supposed to be an error or not? The third sub-bullet of 14.8.2 says
> > that "Attempting to use a type in the qualifier portion of a qualified name
that
> > names a type when that type does not contain the specified member, or if the
> > specified member is not a type where a type is required," results in type
> > deduction failure.
> >
> > The above example directly requires that 'sizeof(new T) - sizeof(T*)' be
> > evaluatated to determine whether the specialization of 'sample' contains the
> > member 'type'.
>
> Indeed, but that doesn't imply that semantic constraints on "new T"
> must be checked (at least not until a POI (point of instantiation) is
> created).
sizeof requires a type or an expression as an operand. 'new T' is not an
expression if T = X. It is not a case of the compiler already knowing the size
of a pointer, the type of 'new X' is not 'X*' it is non-sensical since 'new X'
is not a valid operand of sizeof.
> > Yet this example still compiles. It never says that
> > type-deduction is optional if the result of overload resolution is already
> > known. This example compiling does not follow the order layed out by the
> > standard.
>
> I believe it does.
>
> > Type-deduction *then* overload resolution.
>
> OK.
>
> > Type deduction can either fail or succeed (or error?). In the above
example, type deduction should have
> > failed because of the lack of a member 'type', yet it cannot make the
evaluation
> > to determine that because 'X' has no default ctor.
>
> Not at all: deduction succeeds with T = X. This involves the substitution
> mentioned in the third bullet (not sub-bullet) of 14.8.2/2, which does not
> create an invalid type.
>
> The check that X has a default constructor is not
> mandated, so the partial instantiation (another term for "instantiation of
> a declaration") has no problem creating a candidate check<>(sample<0>::type*).
> That candidate is not selected by overload resolution and therefore no POI
> is created for that candidate, and hence the semantic check that X has a
> default constructor is never done.
You are picking and choosing which semantic checks are performed and which
aren't arbitrarily. The "instantiation of function declaration" terminology is
used in exactly the same way in 14.7.1/1, 14.7.1/5, and 14.8.3, yet clearly
semantic checking is being done, in the case of class member function
declarations, when 'instantiating' the declarations. The terminology is exactly
the same. 14.7.1/1 talks about class member functions, four paragraphs later
(14.7.1/5) talks about template functions that participate in overload
resolution and directly references 14.8.3. I don't see how you can pick and
choose here. When a class template is implicitly instantiated, its member
function declarations are instantiated and, in so doing, are semantically
checked even if they are never instantiated themselves, which is roughly
equivalent to the 'type-deduction' mechanism and overload resolution.
[snip]
> I do agree that you can squint at the standard to make it go either way,
> but I'm quite sure the intent is as explained above. The idea here is
> that adding an nonselected candidate template should not cause a program
> to become ill-formed. At the same time, that candidate must use
> representable types if it is to participate in overload resolution
> (hence the sub-bullet list in 14.8.2/2).
I don't think that you have to squint. :) I see no difference in the
instantiation of a member function declaration and that of a declaration of a
function template specialization. By definition, sizeof operates on a type or
an expression. In our 'recurring' example, the supposed expression 'new X' is
not an expression any more than '2 + my_mythical_function()' is an expression.
'new X' is not an expression because it makes absolutely no semantic sense and
is a direct reference to a non-existent constructor. I don't think that
qualifies as a valid expression. Therefore pre-supposing that the compiler
knows that new always returns pointer, and sizeof(pointer) always equals
sizeof(any_pointer) it is skipping the requirement that sizeof's operand must be
a valid expression.
I see your point as far as what was intended, but the standard clearly
under-specifies what exactly happens in a scenario like this. It isn't a matter
of different interpretation, it is a matter of 'making it up as you see it
should be' <-- both me and you. :) ...which obviously should not be specified
so loosely in the standard. If you were to revise this part of the standard,
what would you say differently? I think the specification of what you say was
the intent would require significantly more complication to produce a more
limited construct. For instance, you would have to redefine 'sizeof' to say
that there are special cases where its operand need not be a valid expression,
as well as list every case where a type can 'go wrong' when instantiated with a
template argument. I think it would be much easier to simplify it to a
standardese version of 'if it makes no sense, it fails type deduction'.
'sizeof(new X)' makes no sense, at least it should make sense to the compiler
because their is nothing special about a default ctor when none exists, even
though we humans recognize it as normal syntax if there theoretically was a
default ctor.
What are you thoughts on a revision such as this?
"All references in the function type of the function template to the
corresponding template parameters are replaced by the specified template
argument values. If a substitution in a template parameter or in the function
type of the function template results in an invalid type, type deduction fails.
Type deduction fails if the resultant function type would not be syntactically
or semantically legal if declared directly at its point of reference."
Or something along those lines. :) You could then qualify it with certain
'special cases' where normal errors are ignored...
-- Attempting to create a cv-qualified reference type.
....etc. I think that would be a lot simpler and would give us some good tools
to work with.
Paul Mensonides
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: google@vandevoorde.com (Daveed Vandevoorde)
Date: Wed, 27 Mar 2002 22:13:01 GMT Raw View
"Richard Smith" <richard@ex-parrot.com> wrote:
> "Daveed Vandevoorde" <google@vandevoorde.com> wrote in message
> news:52f2f9cd.0203230800.19341dc4@posting.google.com...
> > "Paul Mensonides" <pmenso57@attbi.com> wrote:
>
> [ ... ]
>
> > No: deduction _does_ succeed (it's not one of the cases listed as
> > failing deduction) and hence a point of instantiation is created
> > for the first "check()", and that leads to an instantiation error
> > because of the missing default constructor.
>
> Only if you believe it is not an 'invalid type' as mentioned in the preamble
> to the list. The standard does not say that a type is invalid iff it is in
> the list of bullet points in 14.8.2/2.
Yes, it should be clearer. My original response was to clarify that
point: the sub-bullet list of 14.8.2/2 was meant to be exhaustive
(but it known to be lacking).
> > > Basically, it boils down to this: Is type deduction a three result
> (i.e.
> > > succeed, fail, error) mechanism or is it a two result (i.e. succeed,
> fail)
> > > mechanism?
> >
> > It's a binary condition, but what may at first look as a deduction
> > failure is not, and can instead lead to an instantiation failure
> > afterwards.
>
> I'm not sure I understand this. If in some cases deduction succeeds and
> then instantiation fails, what happens? Is an error diagnostic emitted? If
> an error isn't diagnosed, does the specialisation still get added to the
> candidate function set?
There are two kinds of "instantiation". The standard almost always
talks about the kind of instantiation triggered by a point of instantiation
(POI). However, the third bullet of 14.8.2/2 describes a substitution
that is also referred to in 14.7.1/8 as "instantiating a declaration".
The candidate is built using the latter kind of (partial) specialization.
When selected, a POI is created and (full) instantiation occurs,
including all the semantic constraints that apply at that point.
> If an error occurs and is immediately diagnosed, then the system is
> effectively ternary: an error occuring is one possible outcome.
>
> If an error occurs but isn't diagnosed, and the function isn't added to the
> candidate function set, then why bother having a list in 14.8.2/2? There is
> no discernable difference between deduction failing and instantiation
> failing.
>
> If an error occurs but isn't diagnosed, and the function is added to the
> candidate function set, what was the point in instantiating the function?
The goal is to create a candidate for overload resolution. If that
candidate is selected you get an error.
> The compiler might as well not have bothered as the only piece of
> information that could have been extracted from the instantiation (whether
> or not it was successful) has been ignored.
Well it is always "successful" in some sense, but see above.
> > > Can someone clarify this (at least the intent) for me?
> >
> > I remember when the bullet list of 14.8.2/2 was added and clearly the
> > intent was to replace general (somewhat vague) phrasing by an exhaustive
> > list of things where "substitution failure in not an error" (I call it
> > the SFINAE principle in "C++ Templates"). As you note, it's useful for
> > all kinds of classification tests. I suspect the list could be tweaked
> > some more, but the intent is also to limit the amount of substitution
> > that must be done for SFINAE cases.
>
> It seems perverse to want an exhaustive list of situations when type
> deduction can fail. Surely it would be much simpler, and I imagine no
> harder to implement, to simply ditch the list entirely, and only add those
> functions whose declarations can be instantiated to the candidate function
> list.
I don't think it would be simpler or desirable. Ordinary instantiation
is not desirable because that would make programs ill-formed just because
we're trying to create a lesser candidate. What 14.8.2 does is to define
an alternative "partial" instantiation, and the exhaustive list avoids us
having to define semantics (and from an implementation's point of vue:
representations) for new (often nonsensical) types.
> Otherwise, I can see the list getting tweaked again and again as more
> and more increasingly esoteric ways in which substitution can fail are
> discovered.
Unfortunately, that is a challenge.
> The rules on template function overloading are already so
> complicated that even Comeau appears to get them wrong.
Where do you think we get overload resolution wrong?
(Comeau's compiler uses our front end.)
> Surely the simpler
> the better? And in my view trying to come up with an exhaustive list of
> ways in which substitution can fail is unlikely to be simplest approach.
I won't claim there isn't a better way, but that list was put in there to
replace wording that tried to make things simpler, but which in the end
had more holes than ementhaler.
Daveed
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@attbi.com>
Date: Wed, 27 Mar 2002 23:30:34 GMT Raw View
"Daveed Vandevoorde" <google@vandevoorde.com> wrote in message
news:52f2f9cd.0203261846.7f369bc0@posting.google.com...
> > The rules on template function overloading are already so
> > complicated that even Comeau appears to get them wrong.
>
> Where do you think we get overload resolution wrong?
> (Comeau's compiler uses our front end.)
What is supposed to happen here?
template<class T> struct has_template_ABC {
private:
typedef char small_t;
typedef char (& large_t)[256];
template<class U> static small_t check(typename U::template ABC<int>*);
template<class U> static small_t check(typename U::template ABC<int,
int>*);
// etc.
template<class U> static large_t check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(small_t) };
};
struct X {
struct ABC { }; // not a template
};
int main() {
std::cout << has_template_ABC<X>::value << &std::endl;
return 0;
}
Comeau C++ emits this:
error: more than one instance of overloaded function
"has_template_ABC<T>::check [with T=X]" matches the argument list: ...
Secondly, how about this?
template<class T> struct has_nested_ABC {
private:
typedef char small_t;
typedef char (& large_t)[256];
template<class U> static small_t check(typename U::ABC*);
template<class U> static large_t check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(small_t) };
};
struct Y {
template<class> struct ABC { };
// ABC is a template not a type
};
int main() {
std::cout << has_nested_ABC<Y>::value << &std::endl;
return 0;
}
Comeau C++ emits this:
internal error: assertion failed at: "templates.c", line 5623
Lastly, how about this?
// note: these are offset by 1
template<class> char binding();
template<template<class> class> char (& binding())[2];
template<template<class, class> class> char (& binding())[3];
// etc.
template<unsigned> struct checker {
struct type { };
};
template<> struct checker<0U> {
// no 'type'
};
template<class T> struct has_template_ABC {
private:
typedef char small_t;
typedef char (& large_t)[256];
template<class U> static small_t check(typename
checker<sizeof(binding<U::template ABC>()) - 1U>::type*);
template<class U> static large_t check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(small_t) };
};
struct Z {
template<class> struct ABC { };
};
int main() {
std::cout << has_template_ABC<Z>::value << &std::endl;
return 0;
}
Comeau C++ emits this:
error: template instantiation resulted in unexpected function type of
"has_template_ABC<Z>::small_t (checker<1U>::type *)" (the meaning of a name may
have changed since the template declaration -- the type of the template is
"has_template_ABC<Z>::small_t (checker<<template-expr>>::type *)") ...
If I make 'checker<0>' contain 'type' instead of 'checker<unsigned>' Comeau C++
emits this:
error: class "checker<1U>" has no member "type" ...
Apparently, Comeau C++ is ignoring the 'template' keyword in 'U::template ABC',
because it somehow came up with 'checker<1U>'.
??
Paul Mensonides
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@attbi.com>
Date: Thu, 28 Mar 2002 20:00:14 GMT Raw View
"Daveed Vandevoorde" <google@vandevoorde.com> wrote in message
news:52f2f9cd.0203261846.7f369bc0@posting.google.com...
> > The rules on template function overloading are already so
> > complicated that even Comeau appears to get them wrong.
>
> Where do you think we get overload resolution wrong?
> (Comeau's compiler uses our front end.)
What is supposed to happen here?
template<class T> struct has_template_ABC {
private:
typedef char small_t;
typedef char (& large_t)[256];
template<class U> static small_t check(typename U::template ABC<int>*);
template<class U> static small_t check(typename U::template ABC<int,
int>*);
// etc.
template<class U> static large_t check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(small_t) };
};
struct X {
struct ABC { }; // not a template
};
int main() {
std::cout << has_template_ABC<X>::value << &std::endl;
return 0;
}
Comeau C++ emits this:
error: more than one instance of overloaded function
"has_template_ABC<T>::check [with T=X]" matches the argument list: ...
Secondly, how about this?
template<class T> struct has_nested_ABC {
private:
typedef char small_t;
typedef char (& large_t)[256];
template<class U> static small_t check(typename U::ABC*);
template<class U> static large_t check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(small_t) };
};
struct Y {
template<class> struct ABC { };
// ABC is a template not a type
};
int main() {
std::cout << has_nested_ABC<Y>::value << &std::endl;
return 0;
}
Comeau C++ emits this:
internal error: assertion failed at: "templates.c", line 5623
Lastly, how about this?
// note: these are offset by 1
template<class> char binding();
template<template<class> class> char (& binding())[2];
template<template<class, class> class> char (& binding())[3];
// etc.
template<unsigned> struct checker {
struct type { };
};
template<> struct checker<0U> {
// no 'type'
};
template<class T> struct has_template_ABC {
private:
typedef char small_t;
typedef char (& large_t)[256];
template<class U> static small_t check(typename
checker<sizeof(binding<U::template ABC>()) - 1U>::type*);
template<class U> static large_t check(...);
public:
enum { value = sizeof(check<T>(0)) == sizeof(small_t) };
};
struct Z {
template<class> struct ABC { };
};
int main() {
std::cout << has_template_ABC<Z>::value << &std::endl;
return 0;
}
Comeau C++ emits this:
error: template instantiation resulted in unexpected function type of
"has_template_ABC<Z>::small_t (checker<1U>::type *)" (the meaning of a name may
have changed since the template declaration -- the type of the template is
"has_template_ABC<Z>::small_t (checker<<template-expr>>::type *)") ...
If I make 'checker<0>' contain 'type' instead of 'checker<unsigned>' Comeau C++
emits this:
error: class "checker<1U>" has no member "type" ...
Apparently, Comeau C++ is ignoring the 'template' keyword in 'U::template ABC',
because it somehow came up with 'checker<1U>'.
??
Paul Mensonides
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@attbi.com>
Date: Fri, 22 Mar 2002 23:35:34 GMT Raw View
Are there any committee members that can further clarify the intent of 14.8.2
paragraph 2?
We have been attempting (at comp.lang.c++.moderated) to decypher the meaning of
this passage in some somewhat esoteric areas. Basically, the passage indicates
that type deduction can either pass or fail, and that leads to some ambiguity in
what is supposed to happen in cases like the following:
template<unsigned> struct sample { };
struct X {
X(int, int); // no default ctor
};
template<class T> void check( sample<sizeof(new T)>* ) { }
template<class T> void check( int ) { }
int main() {
check<X>(0);
return 0;
}
Obviously, the 'int' argument is a better match for zero, but the question
remains of whether or not this code should have compiled at all. According to
the standard, template type deduction happens before an instantiation of the
function is added to the overload set. If type deduction fails, no
instantiation of the function should be added to the overload set. In the case
above, there is no way that type deduction could possibly succeed. Also, type
deduction does not fail by any of the means that are explicitly listed. This
leads to the question, what does "results in an invalid type" mean? Does it
*only* mean the explicitly listed situations? On Comeau C++, the above example
compiles. But a similar example doesn't:
template<class T> void check( sample<sizeof(new T)>* ) { }
template<class T> void check( ... ) { }
int main() {
check<X>(0);
return 0;
}
It seems to me that, since X has no default ctor, there is absolutely no way
that type deduction can succeed, so maybe it should be eliminated before
overload resolution. In which case, as check(...) is the only one left, it
should be called.
Basically, it boils down to this: Is type deduction a three result (i.e.
succeed, fail, error) mechanism or is it a two result (i.e. succeed, fail)
mechanism? If there are only two possibilities, both examples should have
compiled. However, if there are three than both examples should not have
compiled.
Can someone clarify this (at least the intent) for me?
Paul Mensonides
By the way, the above examples comes from a 'possible' meta-programming
construct to detect whether or not a type is default constructable.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: google@vandevoorde.com (Daveed Vandevoorde)
Date: Sat, 23 Mar 2002 22:10:15 GMT Raw View
"Paul Mensonides" <pmenso57@attbi.com> wrote:
> Are there any committee members that can further clarify the intent of 14.8.2
> paragraph 2?
>
> We have been attempting (at comp.lang.c++.moderated) to decypher the meaning of
> this passage in some somewhat esoteric areas. Basically, the passage indicates
> that type deduction can either pass or fail, and that leads to some ambiguity in
> what is supposed to happen in cases like the following:
>
> template<unsigned> struct sample { };
>
> struct X {
> X(int, int); // no default ctor
> };
>
> template<class T> void check( sample<sizeof(new T)>* ) { }
> template<class T> void check( int ) { }
>
> int main() {
> check<X>(0);
> return 0;
> }
This is fine: deduction succeeds and "check(int)" is instantiated.
> Obviously, the 'int' argument is a better match for zero, but the question
> remains of whether or not this code should have compiled at all. According to
> the standard, template type deduction happens before an instantiation of the
> function is added to the overload set. If type deduction fails, no
> instantiation of the function should be added to the overload set. In the case
> above, there is no way that type deduction could possibly succeed. Also, type
> deduction does not fail by any of the means that are explicitly listed. This
> leads to the question, what does "results in an invalid type" mean? Does it
> *only* mean the explicitly listed situations?
The intent was for the list to be exhaustive, yes. I do suspect it not
to be though (e.g., I recently reported that creating a function type
where the return type is itself a function type should cause deduction
failure).
> On Comeau C++, the above example
> compiles. But a similar example doesn't:
>
> template<class T> void check( sample<sizeof(new T)>* ) { }
> template<class T> void check( ... ) { }
>
> int main() {
> check<X>(0);
> return 0;
> }
>
> It seems to me that, since X has no default ctor, there is absolutely no way
> that type deduction can succeed, so maybe it should be eliminated before
> overload resolution. In which case, as check(...) is the only one left, it
> should be called.
No: deduction _does_ succeed (it's not one of the cases listed as
failing deduction) and hence a point of instantiation is created
for the first "check()", and that leads to an instantiation error
because of the missing default constructor.
> Basically, it boils down to this: Is type deduction a three result (i.e.
> succeed, fail, error) mechanism or is it a two result (i.e. succeed, fail)
> mechanism?
It's a binary condition, but what may at first look as a deduction
failure is not, and can instead lead to an instantiation failure
afterwards.
> If there are only two possibilities, both examples should have
> compiled.
I disagree: see above.
> However, if there are three than both examples should not have
> compiled.
>
> Can someone clarify this (at least the intent) for me?
I remember when the bullet list of 14.8.2/2 was added and clearly the
intent was to replace general (somewhat vague) phrasing by an exhaustive
list of things where "substitution failure in not an error" (I call it
the SFINAE principle in "C++ Templates"). As you note, it's useful for
all kinds of classification tests. I suspect the list could be tweaked
some more, but the intent is also to limit the amount of substitution
that must be done for SFINAE cases.
Daveed
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Richard Smith" <richard@ex-parrot.com>
Date: Sat, 23 Mar 2002 23:46:50 GMT Raw View
"Paul Mensonides" <pmenso57@attbi.com> wrote in message
news:003401c1d1f3$e61ddf20$7772e50c@c161550a...
[ Example 1 (compiles on Comeau): ]
> template<unsigned> struct sample { };
>
> struct X {
> X(int, int); // no default ctor
> };
>
> template<class T> void check( sample<sizeof(new T)>* ) { }
> template<class T> void check( int ) { }
>
> int main() {
> check<X>(0);
> return 0;
> }
[ Example 2 (fails on Comeau): ]
> template<class T> void check( sample<sizeof(new T)>* ) { }
> template<class T> void check( ... ) { }
>
> int main() {
> check<X>(0);
> return 0;
> }
It seems fairly clear what Comeau is doing in this case: it is adding both
to set of candidate functions because it doesn't fit into any of the
explicit cases where type deduction fails in 14.8.2/2 (the nebulous concept
of an 'invalid type' aside). It then constructs the set of viable functions
which includes both examples, without properly validating the template
argument of sample<sizeof(new T)> (an integer has an implicit conversion to
any pointer type, so there is no need to validate the declaration). The
best viable function is then chosen ( the int overload in example 1 and the
sizeof overload in example 2 ). Only when the declaration of the sizeof
overload is actually instantiated does the compiler realise that
sample<sizeof(new T)> is illegal. I'm not entirely sure that this the
correct behaviour, but this seems to be what is happening.
> It seems to me that, since X has no default ctor, there is absolutely no
way
> that type deduction can succeed, so maybe it should be eliminated before
> overload resolution. In which case, as check(...) is the only one left,
it
> should be called.
I think it comes down to what exactly constitutes an 'invalid type'.
> Basically, it boils down to this: Is type deduction a three result (i.e.
> succeed, fail, error) mechanism or is it a two result (i.e. succeed, fail)
> mechanism? If there are only two possibilities, both examples should have
> compiled. However, if there are three than both examples should not have
> compiled.
There's no mention in the standard of a possible third, 'error', outcome.
Either it succeeds or it fails, the question is which it does for the
example
template<class T> void check( sample<sizeof(new T)>* ) { }
and as this is clearly not covered by any of the inner set of bullet points
in 14.8.2/2, it depends soley on whether sample<sizeof(new T)> is an invalid
type. In the absence of a definition of this (and I certainly can't find
one in the standard), I think we have to appeal to common sense, and you're
definition of "anything that that makes no sense for a certain type T but is
syntactically legal and semantically legal for some other type T" seems
pretty reasonable to me.
If we take this definition, sample<sizeof(new T)> is an invalid type and
hence does not enter the set of candidate functions, and both examples ought
to compile.
--
Richard Smith
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@attbi.com>
Date: Sun, 24 Mar 2002 02:27:31 GMT Raw View
"Daveed Vandevoorde" <google@vandevoorde.com> wrote in message
news:52f2f9cd.0203230800.19341dc4@posting.google.com...
[snip]
> > template<unsigned> struct sample { };
> >
> > struct X {
> > X(int, int); // no default ctor
> > };
> >
> > template<class T> void check( sample<sizeof(new T)>* ) { }
> > template<class T> void check( int ) { }
> >
> > int main() {
> > check<X>(0);
> > return 0;
> > }
>
> This is fine: deduction succeeds and "check(int)" is instantiated.
I disagree. 14.8.3 (overload resolution) says (among other things)...
"For each function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to instantiate a
single function template specialization which is added to the candidate
functions set to be used in overload resolution."
Hmm, according to this the specialization happens *before* overload resolution.
Therefore, since it is not possible for argument deduction to fail (supposedly),
this should *not* have compiled.
If the list is exhaustive, then argument deduction should *succeed*, at which
point an instantiation of the first function template (at least the signature)
should have been attempted (which of course, would error). Theoretically, if
that didn't error, it would *then* be added to the overload set. Then the 'int'
version would have been chosen for the better match.
[snip]
> The intent was for the list to be exhaustive, yes. I do suspect it not
> to be though (e.g., I recently reported that creating a function type
> where the return type is itself a function type should cause deduction
> failure).
>
> > On Comeau C++, the above example
> > compiles. But a similar example doesn't:
> >
> > template<class T> void check( sample<sizeof(new T)>* ) { }
> > template<class T> void check( ... ) { }
> >
> > int main() {
> > check<X>(0);
> > return 0;
> > }
> >
[snip]
>
> No: deduction _does_ succeed (it's not one of the cases listed as
> failing deduction) and hence a point of instantiation is created
> for the first "check()", and that leads to an instantiation error
> because of the missing default constructor.
But a specialization is supposed to be instantiated *if* deduction succeeds.
Which, in theory, should have happened in the first example above also.
[snip]
> It's a binary condition, but what may at first look as a deduction
> failure is not, and can instead lead to an instantiation failure
> afterwards.
But *before* overload resolution.
> > If there are only two possibilities, both examples should have
> > compiled.
>
> I disagree: see above.
I counter-disagree: see above. :)
> > However, if there are three than both examples should not have
> > compiled.
> >
> > Can someone clarify this (at least the intent) for me?
>
> I remember when the bullet list of 14.8.2/2 was added and clearly the
> intent was to replace general (somewhat vague) phrasing by an exhaustive
> list of things where "substitution failure in not an error" (I call it
> the SFINAE principle in "C++ Templates"). As you note, it's useful for
> all kinds of classification tests. I suspect the list could be tweaked
> some more, but the intent is also to limit the amount of substitution
> that must be done for SFINAE cases.
What about this example, which *uses* one of the things in the list:
template<unsigned I> struct sample {
// no 'type' here
};
template<> struct sample<0U> {
struct type { };
};
template<class T> void check( typename sample< sizeof(new T) - sizeof(new T)
>::type* ) { }
template<class T> void check( int ) { }
struct X {
X(int, int);
};
int main() {
check<X>(0);
return 0;
}
This *still* compiles with Comeau C++, even though the third bullet says:
"Attempting to use a type in the qualifier portion of a qualified name that
names a type when that type does not contain the specified member, or if the
specified member is not a type where a type is required." There is no way that
it can *know* whether or not sample<...> has a nested type 'type' without
evaluating the 'sizeof(new T)'. At that point, it should have been an error--or
better yet 'deduction failure' and removal from the overload set.
That section should be changed to "results in something that makes no sense."
<-- (except translated into 'standardese'). It should then have bullets that
say when certain things are ignored, such as cv-qualification on a function type
(like the proposed resolution to issue 295).
If something is syntactically and semantically legal, such as all the examples
above, type deduction should never cause overload resolution to fail. The
result of replacing 'T' in 'new T' makes no sense, therefore it should be
removed from the candidate function set. That seems to be the only decent way
to handle this sort of thing.
By the way, I don't disagree that this may have been the intention. However,
the current rules do *not* handle this case in the way that you describe.
Paul Mensonides
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@attbi.com>
Date: Sun, 24 Mar 2002 06:40:05 GMT Raw View
"Richard Smith" <richard@ex-parrot.com> wrote in message
news:1016888062.10928.0.nnrp-01.3e31d362@news.demon.co.uk...
[snip]
> It seems fairly clear what Comeau is doing in this case: it is adding both
> to set of candidate functions because it doesn't fit into any of the
> explicit cases where type deduction fails in 14.8.2/2 (the nebulous concept
> of an 'invalid type' aside). It then constructs the set of viable functions
> which includes both examples, without properly validating the template
> argument of sample<sizeof(new T)> (an integer has an implicit conversion to
> any pointer type, so there is no need to validate the declaration). The
> best viable function is then chosen ( the int overload in example 1 and the
> sizeof overload in example 2 ). Only when the declaration of the sizeof
> overload is actually instantiated does the compiler realise that
> sample<sizeof(new T)> is illegal. I'm not entirely sure that this the
> correct behaviour, but this seems to be what is happening.
The reason I think it wrong is that, according to 14.8.3, ...
"For each function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to instantiate a
single function template specialization which is added to the candidate set to
be used in overload resolution."
My point is that the compiler cannot instantiate the function (i.e. signature)
at all even if overload resolution will pick a different function. So I think
Comeau C++ is 'cheating' on the overload resolution.
[snip]
> I think it comes down to what exactly constitutes an 'invalid type'.
I agree, but I also think it comes down to the order that certain events are
supposed to happen in. According to 14.8.3 (above) it seems clear that this
'instantiation' is supposed to happen prior overload resolution.
> > Basically, it boils down to this: Is type deduction a three result (i.e.
> > succeed, fail, error) mechanism or is it a two result (i.e. succeed, fail)
> > mechanism? If there are only two possibilities, both examples should have
> > compiled. However, if there are three than both examples should not have
> > compiled.
>
> There's no mention in the standard of a possible third, 'error', outcome.
> Either it succeeds or it fails, the question is which it does for the
> example
>
> template<class T> void check( sample<sizeof(new T)>* ) { }
>
> and as this is clearly not covered by any of the inner set of bullet points
> in 14.8.2/2, it depends soley on whether sample<sizeof(new T)> is an invalid
> type. In the absence of a definition of this (and I certainly can't find
> one in the standard), I think we have to appeal to common sense, and you're
> definition of "anything that that makes no sense for a certain type T but is
> syntactically legal and semantically legal for some other type T" seems
> pretty reasonable to me.
:)
Incidently, this also compiles with Comeau C++, though it does in fact directly
use one of the bullets...
template<unsigned I> struct sample {
// no 'type'
};
template<> struct sample<0U> {
struct type { };
};
struct X {
X(int, int) { }
};
template<class T> void check( typename sample< sizeof(new T) - sizeof(new T)
>::type* ) { }
template<class T> void check( int ) { }
int main() {
check<X>(0);
return 0;
}
Something screwy is going on here. It seems like it is bypassing the entire
mechanism because it already knows that 'int' is a better match. Obviously, you
can say that a 'smart' compiler can tell that sizeof aways returns positive, and
if 'x' is positive, 'x' minus 'x' equals zero. I just use that as a sample
because the specialization is simple. I could change it like so... (and it
still compiles)
template<unsigned I, bool = (I >= 256)> struct sample {
// no 'type'
};
template<unsigned I> struct sample<I, true> {
struct type { };
};
struct X {
char unused[254]; // not 256
X(int, int) { }
};
template<class T> void check( typename sample< sizeof(new T) + sizeof(T)
>::type* ) { }
template<class T> void check( int );
int main() {
check<X>(0);
return 0;
}
On my platform, 'sizeof(T*) == 4'. :)
In order to make this test, the compiler must make the calculation 'sizeof(new
T) + sizeof(T)' to determine which specialization of sample to use, and
therefore whether it has a nested type 'type'.
In fact, you could conceivably make an example that exhibits the traits of
nearly *every* bullet in that list, though in every case relies on the
evaluation of something like 'sizeof(new T)'.
[snip]
In any case, I think this part of the standard is very under-defined. Also, in
the interests of keeping things a simple as possible, it would easier to specify
all the cases that are 'okay' rather than all the cases that aren't. I.e. You
could list the few cases where certain things are ignored (like
cv-qualification, etc.) that normally aren't allowed, such as 'const const int'.
Or, cv-qualified function-type, etc.. I think it would be far simpler, and,
conveniently, allow some "traits"-like constructs that currently aren't allowed.
:)
Paul Mensonides
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@attbi.com>
Date: Sun, 24 Mar 2002 22:36:21 CST Raw View
"Richard Smith" <richard@ex-parrot.com> wrote in message
news:1016888062.10928.0.nnrp-01.3e31d362@news.demon.co.uk...
[snip]
> It seems fairly clear what Comeau is doing in this case: it is adding both
> to set of candidate functions because it doesn't fit into any of the
> explicit cases where type deduction fails in 14.8.2/2 (the nebulous concept
> of an 'invalid type' aside). It then constructs the set of viable functions
> which includes both examples, without properly validating the template
> argument of sample<sizeof(new T)> (an integer has an implicit conversion to
> any pointer type, so there is no need to validate the declaration). The
> best viable function is then chosen ( the int overload in example 1 and the
> sizeof overload in example 2 ). Only when the declaration of the sizeof
> overload is actually instantiated does the compiler realise that
> sample<sizeof(new T)> is illegal. I'm not entirely sure that this the
> correct behaviour, but this seems to be what is happening.
The reason I think it wrong is that, according to 14.8.3, ...
"For each function template, if the argument deduction and checking succeeds,
the template-arguments (deduced and/or explicit) are used to instantiate a
single function template specialization which is added to the candidate set to
be used in overload resolution."
My point is that the compiler cannot instantiate the function (i.e. signature)
at all even if overload resolution will pick a different function. So I think
Comeau C++ is 'cheating' on the overload resolution.
[snip]
> I think it comes down to what exactly constitutes an 'invalid type'.
I agree, but I also think it comes down to the order that certain events are
supposed to happen in. According to 14.8.3 (above) it seems clear that this
'instantiation' is supposed to happen prior overload resolution.
> > Basically, it boils down to this: Is type deduction a three result (i.e.
> > succeed, fail, error) mechanism or is it a two result (i.e. succeed, fail)
> > mechanism? If there are only two possibilities, both examples should have
> > compiled. However, if there are three than both examples should not have
> > compiled.
>
> There's no mention in the standard of a possible third, 'error', outcome.
> Either it succeeds or it fails, the question is which it does for the
> example
>
> template<class T> void check( sample<sizeof(new T)>* ) { }
>
> and as this is clearly not covered by any of the inner set of bullet points
> in 14.8.2/2, it depends soley on whether sample<sizeof(new T)> is an invalid
> type. In the absence of a definition of this (and I certainly can't find
> one in the standard), I think we have to appeal to common sense, and you're
> definition of "anything that that makes no sense for a certain type T but is
> syntactically legal and semantically legal for some other type T" seems
> pretty reasonable to me.
:)
Incidently, this also compiles with Comeau C++, though it does in fact directly
use one of the bullets...
template<unsigned I> struct sample {
// no 'type'
};
template<> struct sample<0U> {
struct type { };
};
struct X {
X(int, int) { }
};
template<class T> void check( typename sample< sizeof(new T) - sizeof(new T)
>::type* ) { }
template<class T> void check( int ) { }
int main() {
check<X>(0);
return 0;
}
Something screwy is going on here. It seems like it is bypassing the entire
mechanism because it already knows that 'int' is a better match. Obviously, you
can say that a 'smart' compiler can tell that sizeof aways returns positive, and
if 'x' is positive, 'x' minus 'x' equals zero. I just use that as a sample
because the specialization is simple. I could change it like so... (and it
still compiles)
template<unsigned I, bool = (I >= 256)> struct sample {
// no 'type'
};
template<unsigned I> struct sample<I, true> {
struct type { };
};
struct X {
char unused[254]; // not 256
X(int, int) { }
};
template<class T> void check( typename sample< sizeof(new T) + sizeof(T)
>::type* ) { }
template<class T> void check( int );
int main() {
check<X>(0);
return 0;
}
On my platform, 'sizeof(T*) == 4'. :)
In order to make this test, the compiler must make the calculation 'sizeof(new
T) + sizeof(T)' to determine which specialization of sample to use, and
therefore whether it has a nested type 'type'.
In fact, you could conceivably make an example that exhibits the traits of
nearly *every* bullet in that list, though in every case relies on the
evaluation of something like 'sizeof(new T)'.
[snip]
In any case, I think this part of the standard is very under-defined. Also, in
the interests of keeping things a simple as possible, it would easier to specify
all the cases that are 'okay' rather than all the cases that aren't. I.e. You
could list the few cases where certain things are ignored (like
cv-qualification, etc.) that normally aren't allowed, such as 'const const int'.
Or, cv-qualified function-type, etc.. I think it would be far simpler, and,
conveniently, allow some "traits"-like constructs that currently aren't allowed.
:)
Paul Mensonides
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Richard Smith" <richard@ex-parrot.com>
Date: Mon, 25 Mar 2002 19:17:48 GMT Raw View
"Daveed Vandevoorde" <google@vandevoorde.com> wrote in message
news:52f2f9cd.0203230800.19341dc4@posting.google.com...
> "Paul Mensonides" <pmenso57@attbi.com> wrote:
[ ... ]
> No: deduction _does_ succeed (it's not one of the cases listed as
> failing deduction) and hence a point of instantiation is created
> for the first "check()", and that leads to an instantiation error
> because of the missing default constructor.
Only if you believe it is not an 'invalid type' as mentioned in the preamble
to the list. The standard does not say that a type is invalid iff it is in
the list of bullet points in 14.8.2/2.
> > Basically, it boils down to this: Is type deduction a three result
(i.e.
> > succeed, fail, error) mechanism or is it a two result (i.e. succeed,
fail)
> > mechanism?
>
> It's a binary condition, but what may at first look as a deduction
> failure is not, and can instead lead to an instantiation failure
> afterwards.
I'm not sure I understand this. If in some cases deduction succeeds and
then instantiation fails, what happens? Is an error diagnostic emitted? If
an error isn't diagnosed, does the specialisation still get added to the
candidate function set?
If an error occurs and is immediately diagnosed, then the system is
effectively ternary: an error occuring is one possible outcome.
If an error occurs but isn't diagnosed, and the function isn't added to the
candidate function set, then why bother having a list in 14.8.2/2? There is
no discernable difference between deduction failing and instantiation
failing.
If an error occurs but isn't diagnosed, and the function is added to the
candidate function set, what was the point in instantiating the function?
The compiler might as well not have bothered as the only piece of
information that could have been extracted from the instantiation (whether
or not it was successful) has been ignored.
> > Can someone clarify this (at least the intent) for me?
>
> I remember when the bullet list of 14.8.2/2 was added and clearly the
> intent was to replace general (somewhat vague) phrasing by an exhaustive
> list of things where "substitution failure in not an error" (I call it
> the SFINAE principle in "C++ Templates"). As you note, it's useful for
> all kinds of classification tests. I suspect the list could be tweaked
> some more, but the intent is also to limit the amount of substitution
> that must be done for SFINAE cases.
It seems perverse to want an exhaustive list of situations when type
deduction can fail. Surely it would be much simpler, and I imagine no
harder to implement, to simply ditch the list entirely, and only add those
functions whose declarations can be instantiated to the candidate function
list. Otherwise, I can see the list getting tweaked again and again as more
and more increasingly esoteric ways in which substitution can fail are
discovered. The rules on template function overloading are already so
complicated that even Comeau appears to get them wrong. Surely the simpler
the better? And in my view trying to come up with an exhaustive list of
ways in which substitution can fail is unlikely to be simplest approach.
--
Richard Smith
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: google@vandevoorde.com (Daveed Vandevoorde)
Date: Mon, 25 Mar 2002 21:03:54 GMT Raw View
"Paul Mensonides" <pmenso57@attbi.com> wrote:
> "Daveed Vandevoorde" <google@vandevoorde.com> wrote:
[...]
> > > template<unsigned> struct sample { };
> > >
> > > struct X {
> > > X(int, int); // no default ctor
> > > };
> > >
> > > template<class T> void check( sample<sizeof(new T)>* ) { }
> > > template<class T> void check( int ) { }
> > >
> > > int main() {
> > > check<X>(0);
> > > return 0;
> > > }
> >
> > This is fine: deduction succeeds and "check(int)" is instantiated.
>
> I disagree. 14.8.3 (overload resolution) says (among other things)...
>
> "For each function template, if the argument deduction and checking succeeds,
> the template-arguments (deduced and/or explicit) are used to instantiate a
> single function template specialization which is added to the candidate
> functions set to be used in overload resolution."
>
> Hmm, according to this the specialization happens *before* overload
> resolution.
> Therefore, since it is not possible for argument deduction to fail (supposedly),
> this should *not* have compiled.
Why not?
> If the list is exhaustive, then argument deduction should *succeed*, at which
> point an instantiation of the first function template (at least the signature)
> should have been attempted (which of course, would error).
Argument deduction succeeds and presumably results in a valid
parameter type "sample<some-value>" (which allows the "partial
instantiation" of 14.8.3). I understand 14.7.1/5 to say
that "sample<sizeof(new T)>" need not be instantiated if the
overload resolution process can determine the result without
that instantiation.
I'm actually not quite sure whether a diagnostic is _allowed_
here; it seems it is from 14.7.1/5.
Daveed
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Richard Smith" <richard@ex-parrot.com>
Date: Mon, 25 Mar 2002 21:04:21 GMT Raw View
"Paul Mensonides" <pmenso57@attbi.com> wrote in message
news:_W9n8.114072$uA5.92516@rwcrnsc51.ops.asp.att.net...
> "Richard Smith" <richard@ex-parrot.com> wrote in message
> news:1016888062.10928.0.nnrp-01.3e31d362@news.demon.co.uk...
>
> [snip]
>
> > I'm not entirely sure that this the
> > correct behaviour, but this seems to be what is happening.
>
> The reason I think it wrong is that, according to 14.8.3, ...
>
> "For each function template, if the argument deduction and checking
succeeds,
> the template-arguments (deduced and/or explicit) are used to instantiate a
> single function template specialization which is added to the candidate
set to
> be used in overload resolution."
I'd not noticed that sentence before. Now I understand you're reasoning.
This raises another ambiguity -- does only the function template's
declaration get instantiated or does it's definition too? I find it hard to
believe the intention is for the definition to get instatiated just because
the function is in the candidate function set.
> My point is that the compiler cannot instantiate the function (i.e.
signature)
> at all even if overload resolution will pick a different function. So I
think
> Comeau C++ is 'cheating' on the overload resolution.
OK. I think I agree with that. So irrespective of whether the 'sizeof'
should ever be excluded from the candidate function set, Comeau is getting
it wrong. Let me check we're both now in agreement about this:
If type deduction is allowed to fail for the 'sizeof' overload (i.e. if
'helper<sizeof(new X)>' is an invalid type), then both examples compile; if
type deduction can never fail for the 'sizeof' overload, 14.8.3 should cause
at least the declarations of both specialisations of functions to be
instatiated, and hence both examples should fail.
> [snip]
>
> > I think it comes down to what exactly constitutes an 'invalid type'.
>
> I agree, but I also think it comes down to the order that certain events
are
> supposed to happen in. According to 14.8.3 (above) it seems clear that
this
> 'instantiation' is supposed to happen prior overload resolution.
Now that you've pointed out the sentence in 14.8.3 about instantiation, it
seems to me clearer that the 'invalid type' should probably prevent *any*
overload entering the candidate function set if it's declaration cannot be
instantiated.
> > > Basically, it boils down to this: Is type deduction a three result
(i.e.
> > > succeed, fail, error) mechanism or is it a two result (i.e. succeed,
fail)
> > > mechanism? If there are only two possibilities, both examples should
have
> > > compiled. However, if there are three than both examples should not
have
> > > compiled.
> >
> > There's no mention in the standard of a possible third, 'error',
outcome.
OK. Maybe I was wrong. There's no *explicit* mention, but I supose failing
to instantiate the declaration of a member of the candidate function set
could produce this effect.
[ ... ]
> Incidently, this also compiles with Comeau C++, though it does in fact
directly
> use one of the bullets...
But we've already estabilished that somewhere along the lines Comeau is
cheating. ;-)
> template<unsigned I> struct sample {
> // no 'type'
> };
>
> template<> struct sample<0U> {
> struct type { };
> };
>
> struct X {
> X(int, int) { }
> };
>
> template<class T> void check( typename sample< sizeof(new T) - sizeof(new
T)
> >::type* ) { }
> template<class T> void check( int ) { }
>
> int main() {
> check<X>(0);
> return 0;
> }
>
> Something screwy is going on here.
I can believe that. It took me at least two cups of tea before I was sure
that I understood what was happening ;-). I suspect Comeau is just not
bothering to instantiate the declaration of the 'sizeof' overload again.
> It seems like it is bypassing the entire
> mechanism because it already knows that 'int' is a better match.
Obviously, you
> can say that a 'smart' compiler can tell that sizeof aways returns
positive, and
> if 'x' is positive, 'x' minus 'x' equals zero. I just use that as a
sample
> because the specialization is simple. I could change it like so... (and
it
> still compiles)
[ more esoteric example ]
> In order to make this test, the compiler must make the calculation
'sizeof(new
> T) + sizeof(T)' to determine which specialization of sample to use, and
> therefore whether it has a nested type 'type'.
You could see how the compiler might just say 'sample<...>::type *' is
either a pointer to a nested type in which case it is a less good conversion
that the int's exact match, or it's a non-existant nested type in which case
it is excluded from the set of CFs by 14.8.2/2. Either way the int is a
better match. I know that this isn't illegal, and I agree with you that
this shouldn't compile, but I imagine that Comeau is doing something similar
to this.
Incidentally, it also compiles if you move the nested 'struct type {};' to
the primary template, although this is correct as it should be omitted from
the set of CFs by 14.8.2/2.
[ ... ]
> In any case, I think this part of the standard is very under-defined.
Agreed. At some point I think a DR on this might be in order, although I
suspect there may be more aspects to this that still need exploring.
> Also, in
> the interests of keeping things a simple as possible, it would easier to
specify
> all the cases that are 'okay' rather than all the cases that aren't. I.e.
You
> could list the few cases where certain things are ignored (like
> cv-qualification, etc.) that normally aren't allowed, such as 'const const
int'.
> Or, cv-qualified function-type, etc.. I think it would be far simpler,
and,
> conveniently, allow some "traits"-like constructs that currently aren't
allowed.
> :)
If the relevant bit of 14.8.3/1 were modified to say
"if the argument deduction and checking succeeds, the template-arguments
... are used to instantiate the declaration of a single function
template
specialisation. If this declaration is successfully instantiated it is
added to the candidate function set to be used in overload resolution.
If
the declaration is not successfully instantiated, no diagnostics are
issued
for the failed instantiation and no function is added to the candidate
function set".
then there would be no need to have the list in 14.8.2/2; the rules would be
simpler (and I can't believe any harder to implement); and we would get some
nice new traits classes.
--
Richard Smith
--
Richard Smith
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Paul Mensonides" <pmenso57@attbi.com>
Date: Tue, 26 Mar 2002 01:36:41 GMT Raw View
"Daveed Vandevoorde" <google@vandevoorde.com> wrote in message
news:52f2f9cd.0203241844.342c948b@posting.google.com...
> > I disagree. 14.8.3 (overload resolution) says (among other things)...
> >
> > "For each function template, if the argument deduction and checking
succeeds,
> > the template-arguments (deduced and/or explicit) are used to instantiate a
> > single function template specialization which is added to the candidate
> > functions set to be used in overload resolution."
> >
> > Hmm, according to this the specialization happens *before* overload
> > resolution.
> > Therefore, since it is not possible for argument deduction to fail
(supposedly),
> > this should *not* have compiled.
>
> Why not?
There is an order of occurance here:
I. explicit arguments are given to the template function
II. type deduction occurs
A. type deduction succeeds
1. function declaration is instantiated
2. function is added to candidate set
B. type deduction fails
1. no instantiation of function is added to candidate set
III. overload resolution occurs
A. if selected, function definition is instantiated
According to 14.8.3, *if* type deduction succeeds, '3a' would have to be an
error, which would break the entire candidate set. This order is absolutely
defined in the standard to be this way, whether or not that was the intention.
> > If the list is exhaustive, then argument deduction should *succeed*, at
which
> > point an instantiation of the first function template (at least the
signature)
> > should have been attempted (which of course, would error).
>
> Argument deduction succeeds and presumably results in a valid
> parameter type "sample<some-value>" (which allows the "partial
> instantiation" of 14.8.3). I understand 14.7.1/5 to say
> that "sample<sizeof(new T)>" need not be instantiated if the
> overload resolution process can determine the result without
> that instantiation.
14.7.1/5 is about instantiations of 'class' template definitions, not function
template declarations.
14.7.1/8 says...
"If a function template or a member function template specialization is used in
a way that involves overload resolution, a declaration of the specialization is
implicitly instantiated (14.8.3)."
That doesn't leave much to the imagination.
> I'm actually not quite sure whether a diagnostic is _allowed_
> here; it seems it is from 14.7.1/5.
What about my other example?
template<unsigned> struct sample {
// no type
};
template<> struct sample<0U> {
struct type { };
};
template<class T> void check( typename sample<sizeof(new T) -
sizeof(T*)>::type* ) { }
template<class T> void check( int ) { }
struct X {
X(int, int) { } // no default ctor
};
int main(int argc, char* argv[]) {
check<X>(0);
return 0;
}
Is this supposed to be an error or not? The third sub-bullet of 14.8.2 says
that "Attempting to use a type in the qualifier portion of a qualified name that
names a type when that type does not contain the specified member, or if the
specified member is not a type where a type is required," results in type
deduction failure.
The above example directly requires that 'sizeof(new T) - sizeof(T*)' be
evaluatated to determine whether the specialization of 'sample' contains the
member 'type'. Yet this example still compiles. It never says that
type-deduction is optional if the result of overload resolution is already
known. This example compiling does not follow the order layed out by the
standard. Type-deduction *then* overload resolution. Type deduction can either
fail or succeed (or error?). In the above example, type deduction should have
failed because of the lack of a member 'type', yet it cannot make the evaluation
to determine that because 'X' has no default ctor. So what happens? Does type
deduction somehow succeed (when it shouldn't), or does it fail because type
deduction found something that made no sense? As I've pointed out several
times, this is supposed to happen *before* overload resolution. The standard
does not adequately describe what should happen in a case such as this,
therefore since type-deduction cannot succeed (by the quoted bullet) yet cannot
fail because it can't evaluate 'sizeof(new T)'. The only thing left is
compile-time error (or removal from the overload set, which would be the logical
thing to do).
Paul Mensonides
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "James Kuyper Jr." <kuyper@wizard.net>
Date: Tue, 26 Mar 2002 15:49:34 GMT Raw View
Daveed Vandevoorde wrote:
....
> I'm actually not quite sure whether a diagnostic is _allowed_
> here; it seems it is from 14.7.1/5.
Diagnostics are always allowed, even spurious ones.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: Gabriel Dos Reis <dosreis@cmla.ens-cachan.fr>
Date: Tue, 26 Mar 2002 14:30:38 CST Raw View
"James Kuyper Jr." <kuyper@wizard.net> writes:
| Daveed Vandevoorde wrote:
| ....
| > I'm actually not quite sure whether a diagnostic is _allowed_
| > here; it seems it is from 14.7.1/5.
|
| Diagnostics are always allowed, even spurious ones.
But spurious diagnostics aren't useful.
Certainly, the standard doesn't mandate useful implementations, but
rhetorics aren't enlightening in a thread about "clarification" :-)
--
Gabriel Dos Reis, dosreis@cmla.ens-cachan.fr
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: google@vandevoorde.com (Daveed Vandevoorde)
Date: Tue, 26 Mar 2002 20:26:30 CST Raw View
"Paul Mensonides" <pmenso57@attbi.com> wrote:
> "Daveed Vandevoorde" <google@vandevoorde.com> wrote:
[...]
> > > Hmm, according to this the specialization happens *before* overload
> > > resolution.
> > > Therefore, since it is not possible for argument deduction to fail
> (supposedly),
> > > this should *not* have compiled.
> >
> > Why not?
>
> There is an order of occurance here:
>
> I. explicit arguments are given to the template function
> II. type deduction occurs
> A. type deduction succeeds
> 1. function declaration is instantiated
> 2. function is added to candidate set
> B. type deduction fails
> 1. no instantiation of function is added to candidate set
> III. overload resolution occurs
> A. if selected, function definition is instantiated
Sure.
> According to 14.8.3, *if* type deduction succeeds, '3a' would have to be an
> error, which would break the entire candidate set. This order is absolutely
> defined in the standard to be this way, whether or not that was the
> intention.
I don't think the issue is the order in which things occur. Instead,
I believe the core issue is the notion of "instantiating a function
declaration", which is part of the deduction process and described in
the third bullet of 14.8.2/2. The goal of this "partial instantiation"
is to create a candidate; it does not require the semantic checks
associated with a normal instantiation.
> > > If the list is exhaustive, then argument deduction should *succeed*, at
> which
> > > point an instantiation of the first function template (at least the
> signature)
> > > should have been attempted (which of course, would error).
> >
> > Argument deduction succeeds and presumably results in a valid
> > parameter type "sample<some-value>" (which allows the "partial
> > instantiation" of 14.8.3). I understand 14.7.1/5 to say
> > that "sample<sizeof(new T)>" need not be instantiated if the
> > overload resolution process can determine the result without
> > that instantiation.
>
> 14.7.1/5 is about instantiations of 'class' template definitions,
> not function template declarations.
Yes, I wrote nonsense there.
> 14.7.1/8 says...
>
> "If a function template or a member function template specialization is used in
> a way that involves overload resolution, a declaration of the specialization is
> implicitly instantiated (14.8.3)."
>
> That doesn't leave much to the imagination.
I agree that the order of things is quite clear, but that doesn't
mean that sizeof(new T) is an error during the substitution process;
no invalid type is created here.
> > I'm actually not quite sure whether a diagnostic is _allowed_
> > here; it seems it is from 14.7.1/5.
>
> What about my other example?
>
> template<unsigned> struct sample {
> // no type
> };
>
> template<> struct sample<0U> {
> struct type { };
> };
>
> template<class T> void check( typename sample<sizeof(new T) -
> sizeof(T*)>::type* ) { }
> template<class T> void check( int ) { }
>
> struct X {
> X(int, int) { } // no default ctor
> };
>
> int main(int argc, char* argv[]) {
> check<X>(0);
> return 0;
> }
>
> Is this supposed to be an error or not? The third sub-bullet of 14.8.2 says
> that "Attempting to use a type in the qualifier portion of a qualified name that
> names a type when that type does not contain the specified member, or if the
> specified member is not a type where a type is required," results in type
> deduction failure.
>
> The above example directly requires that 'sizeof(new T) - sizeof(T*)' be
> evaluatated to determine whether the specialization of 'sample' contains the
> member 'type'.
Indeed, but that doesn't imply that semantic constraints on "new T"
must be checked (at least not until a POI (point of instantiation) is
created).
> Yet this example still compiles. It never says that
> type-deduction is optional if the result of overload resolution is already
> known. This example compiling does not follow the order layed out by the
> standard.
I believe it does.
> Type-deduction *then* overload resolution.
OK.
> Type deduction can either fail or succeed (or error?). In the above example, type deduction should have
> failed because of the lack of a member 'type', yet it cannot make the evaluation
> to determine that because 'X' has no default ctor.
Not at all: deduction succeeds with T = X. This involves the substitution
mentioned in the third bullet (not sub-bullet) of 14.8.2/2, which does not
create an invalid type. The check that X has a default constructor is not
mandated, so the partial instantiation (another term for "instantiation of
a declaration") has no problem creating a candidate check<>(sample<0>::type*).
That candidate is not selected by overload resolution and therefore no POI
is created for that candidate, and hence the semantic check that X has a
default constructor is never done.
> So what happens? Does type
> deduction somehow succeed (when it shouldn't), or does it fail because type
> deduction found something that made no sense? As I've pointed out several
> times, this is supposed to happen *before* overload resolution. The standard
> does not adequately describe what should happen in a case such as this,
> therefore since type-deduction cannot succeed (by the quoted bullet) yet cannot
> fail because it can't evaluate 'sizeof(new T)'. The only thing left is
> compile-time error (or removal from the overload set, which would be the logical
> thing to do).
See my analysis above.
I do agree that you can squint at the standard to make it go either way,
but I'm quite sure the intent is as explained above. The idea here is
that adding an nonselected candidate template should not cause a program
to become ill-formed. At the same time, that candidate must use
representable types if it is to participate in overload resolution
(hence the sub-bullet list in 14.8.2/2).
Daveed
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]