|
Boost : |
From: Lois Goldthwaite (loisg_at_[hidden])
Date: 2001-01-03 09:08:39
> Message: 8
> Date: Tue, 2 Jan 2001 09:03:03 -0500
> From: "David Abrahams" <abrahams_at_[hidden]>
> Subject: Re: Re: Problems compiling graph library on IRIX.
>
> Lois,
>
> Do you also disagree with John's reading of 14.7.1 paragraph 2?
>
> -Dave
Well, I've always found Clause 14 to be particularly thorny and
impenetrable, and I won't contest legalisms with anyone who claims
authority. To save everyone looking it up, here's the paragraph in
question:
-2- Unless a function template specialization has been explicitly
instantiated or explicitly specialized, the function
template specialization is implicitly instantiated when the
specialization is referenced in a context that requires a function
definition to exist. Unless a call is to a function template explicit
specialization or to a member function of an explicitly
specialized class template, a default argument for a function template
or a member function of a class template is
implicitly instantiated when the function is called in a context that
requires the value of the default argument.
And here's 8.3.6p5 on default arguments:
-5- A default argument expression is implicitly converted (clause conv)
to the parameter type. The default argument
expression has the same semantic constraints as the initializer
expression in a declaration of a variable of the parameter
type, using the copy-initialization semantics (dcl.init). The names in
the expression are bound, and the semantic
constraints are checked, at the point where the default argument
expression appears. Name lookup and checking of
semantic constraints for default arguments in function templates and in
member functions of class templates are
performed as described in 14.7.1.
And here's the original code:
> struct Null
> {
> };
>
> template <class T>
> class Foo
> {
> public:
> Foo( T t = Null() ) {}
> };
>
> template <class T>
> Foo<T>
> MakeFoo( T t )
> {
> return Foo<T>(t);
> };
>
> main()
> {
> // Bad on IRIX
> int i = 1;
> MakeFoo<int>( i );
>
> // OK on IRIX;
> // Null i;
> // MakeFoo<Null>(i);
>
> return 0;
> }
So it comes down to this:
The names in the expression are bound, and the semantic constraints are
checked,
*at the point where the default argument expression appears*
vs this:
a default argument for a function template or a member function
of a class template is implicitly instantiated when the function is
called
*in a context that requires the value of the default argument*
Wasn't this the issue that we had to wrestle with just before the
Standard was finalized? A lot of the container member functions had
default arguments of T():
void resize(size_type sz, T c = T());
and there was a lot of discussion as to whether the library was wrong to
specify things this way, or whether the core was wrong to require
checking for the existence of a default ctor unless it was actually used
in context. (I think Sean Corfield even proposed adding a paragraph
stating "This document shall be construed AS IF it had been properly
specified using overloaded functions instead of default arguments..."
:-) I believe 14.7.1p5 is the resolution for that issue, but I'm not
inclined to dig back through prior (and even more impenetrable!)
versions of Clause 14 to verify that.
Having said all that, I'm not sure that
Foo( T t = Null() ) {}
exactly fits the case of requiring "instantiation," since Null itself is
not a template class. Since no way is given in the example to convert a
Null to any other type, the default argument would be *totally useless*
for all instantiations except for Foo<Null>. Rather, this appears to me
to be an effort to specify a default template argument for class Foo,
and the syntax for that is
template <class T = Null >
class Foo
{
public:
Foo( T t = T() ) {}
};
[Therefore declaring a "Foo x;" or "Foo()" means you get a Foo<Null>. If
you should want to have that minor convenience. I think I'd rather use a
typedef if there's only one parameter ever needed, but that's a question
of style.]
So my suggestion was based more on how to achieve the perceived (by me)
intent, rather than on expertise as a language lawyer. Andrew confirms
that it works in his situation. I'm assuming that if there were a
Null-to-int conversion, then the compilers would be happy with the
original (but I didn't check it).
For a definitive ruling on whether the compilers should ignore or
complain about the unused and useless non-dependent default argument, I
will humbly defer to the core wizards. Is this worth filing an issue?
Lois
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk