Topic: Incomplete type in template parameter, complete type as
Author: greghe@pacbell.net (Greg Herlihy)
Date: Thu, 12 Apr 2007 23:42:55 GMT Raw View
On 4/11/07 9:01 AM, in article
1176302440.110184.69550@d57g2000hsg.googlegroups.com, "James Kanze"
<james.kanze@gmail.com> wrote:
> On Apr 9, 3:35 am, gre...@pacbell.net (Greg Herlihy) wrote:
>> No, an unbounded array type is not at all "compatible" with a
>> bounded array type
>=20
> You mean we've broken something that fundamental? It certainly
> is in C, and IIRC, something like the following worked in
> CFront:
>=20
> void f( int (&array)[] ) ;
>=20
> void g()
> {
> int a[ 3 ] ;
> f( a ) ;
> }
>=20
> There's no reason why it shouldn't work, and it seems a bit
> broken if it doesn't. (Of the two compilers I have access to,
> one accepts it, the other doesn't.)
>=20
>> - they are distinct and completely different types - as the following
>> program demonstrates (confirmed with gcc, Comeau and EDG):
> ...
> That I can understand. You can't use an incomplete type where a
> complete type is required. The reverse should work.
No, switching the bounded and unbounded array types around - also fails -
and with the same error message as before:
template <int (&a)[]>
void f() {}
int s[] =3D { 1 };
int main()
{
f<s>(); // error: no matching function for call to 'f'
}
>> Moving the definition of "s" before main() does compile successfully (=
again,
>> on all three C++ compilers tested), because moving the definition actu=
ally
>> changes the type of "s". Unlike forwardly-declared types (which have a
>> consistent type before and after the type is completed), the type of a=
n
>> array object actually changes once the array's type is completely defi=
ned.
>> That is the significant point that you do not seem to have realized ju=
st
>> yet.
>=20
> I realize it, that's why I spoke of "compatible" types, and not
> the same type. (And I know, that actual concept of compatible
> types is from the C standard, not from C++.)
C++ has not retained C's notion of "compatible types" - at least not as f=
ar
as templates are concerned.
=20
>> Furthermore, there is no such thing as "compatible" types when it come=
s to
>> templates.
>=20
> That is the crux of my question. If g++ had refused to
> instantiation the template in my example, I would put it down to
> that. What it did, however, was generate error messages from
> the instantiation.
No, the compiler is not instantiating the function template accept() (and
the error that the gcc reports in the original program - is not an
instantiation failure.) Instead, the compiler has simply detected (during
the course of resolving the accept() function call) that the definition o=
f
the accept() template function is ill-formed.
According to =A714.6/7, the compiler may issue a diagnostic message for a=
n
ill-formed (even if never instantiated) template definition. (In fact, th=
e
point at which a compiler detects and reports this kind of error is left =
up
to the compiler as a "qualify of implementation" issue). As an example:
void Begin( int (&p)[1])
{
}
extern int s[];
=20
template < int N>
void f()
{
Begin(s); // Error: invalid initialization of reference
}
=20
int s[] =3D { 1 };
int main()
{
}
In the above program, the gcc compiler reports an error in the f() functi=
on
template - even though the function template f() in which the error is fo=
und
- is never instantiated at all. The issue in the original program is
comparable: gcc found an error in accept() - even though the accept()
function template was not instantiated.
So the conclusion here is simply that bounded and unbounded array types a=
re
not at all alike in C++. So C++ programs that deal with those two types -
must accommodate the difference.
Greg =20
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: greghe@pacbell.net (Greg Herlihy)
Date: Sun, 8 Apr 2007 01:27:36 GMT Raw View
On 4/6/07 8:59 AM, in article
1175859305.420565.301320@y80g2000hsf.googlegroups.com, "James Kanze"
<james.kanze@gmail.com> wrote:
> I've got a somewhat strange situtation, and while I have a
> solution, I'm curious about the legality of my first attempted
> solution, purely templates. (G++ doesn't compile it, but I
> can't decide whether the problem is in my code, or a bug in
> g++). Basically, the code is:
>=20
=2E
> template< char const* (&keys)[] >
> bool
> accept(
> char const* key )
> {
> return std::find_if( begin( keys ), end( keys ),
> Match( key ) )
> !=3D end( keys ) ;
> }
As noted, the template function keys(), specifies an incomplete type (an
unbouned array type) as its nontype template parameter.
=20
> char const* tab1[] =3D
> {
> "Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Nl", "No"
> } ;
>=20
> char const* tab2[] =3D
> {
> "Lu", "Ll", "Lt", "Lm", "Lo"
> } ;
>=20
> struct Table
> {
> int id ;
> bool (* fnc)( char const* key ) ;
> } ;
>=20
> Table const tbl[] =3D
> {
> { 1, &accept< tab1 > },
> { 2, &accept< tab2 > },
> } ;
The tbl array attempts to instantiate the accept() function template with
two bounded arrays types (meaning that in each instance the nontype argum=
ent
is a complete type). The attempt to instantiate accept() fails (and fails
correctly) because the nontype argument used to instantiate accept() in b=
oth
cases is not the same type as the type specified in accept's function
template declaration. In other words, an incomplete array type is a
different type than a complete array type:
"The declared type of an array object might be an array of unknown size a=
nd
therefore be incomplete at one point in a translation unit and complete
later on; the array types at those two points (=B3array of unknown bound =
of
T=B2 and =B3array of N[T]=B2) are different types. The type of a pointer =
to array
of unknown size, or of a type defined by a typedef declaration to be an
array of unknown size, cannot be completed." [=A73.9/7]
So in order to instantiate accept() with tab1 and tab2, it is necessary t=
o
ensure that their types are complete only after accept() has been
instantiated:=20
extern char const* tab1[];
extern char const* tab2[];
=20
struct Table=20
{=20
int id ;
bool (* fnc)( char const* key ) ;
} ;=20
Table const tbl[] =3D
{=20
{ 1, &accept< tab1 > }, // OK, tab1 has incomplete type
{ 2, &accept< tab2 > },
} ;=20
char const* tab1[]=3D{"Lu","Ll", "Lt","Lm","Lo","Nd","Nl","No"};
char const* tab2[] =3D{"Lu", "Ll", "Lt", "Lm", "Lo" } ;
Unfortunately, the program still fails to compile because accept() uses i=
ts
own nontype template argument ("keys" which is an incomplete type) as a
function parameter in calls to the function templates begin() and end() -
both of which use the function parameter's type to deduce a complete arra=
y
type - a deduction which naturally fails when the parameter's type is
certain to be incomplete.
Greg
=20
---
[ 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.comeaucomputing.com/csc/faq.html ]
Author: greghe@pacbell.net (Greg Herlihy)
Date: Mon, 9 Apr 2007 01:35:41 GMT Raw View
On 4/8/07 1:17 PM, in article
1176052237.259633.230510@o5g2000hsb.googlegroups.com, "James Kanze"
<james.kanze@gmail.com> wrote:
> On Apr 8, 3:27 am, gre...@pacbell.net (Greg Herlihy) wrote:
>> On 4/6/07 8:59 AM, in article
>> 1175859305.420565.301...@y80g2000hsf.googlegroups.com, "James Kanze"
>=20
>> <james.ka...@gmail.com> wrote:
>> The tbl array attempts to instantiate the accept() function template w=
ith
>> two bounded arrays types (meaning that in each instance the nontype ar=
gument
>> is a complete type). The attempt to instantiate accept() fails (and fa=
ils
>> correctly) because the nontype argument used to instantiate accept() i=
n both
>> cases is not the same type as the type specified in accept's function
>> template declaration.
>=20
> The instantiation doesn't fail, and if I modify the code to use
> a sentinal value, rather that "begin()" and "end()", it works
> perfectly. char const* [N] (with N a compile time constant) is
> "compatible" with char const* [].
No, an unbounded array type is not at all "compatible" with a bounded arr=
ay
type - they are distinct and completely different types - as the followin=
g
program demonstrates (confirmed with gcc, Comeau and EDG):
template <int N, int (&a)[N]>
void f() {}
extern int s[];
=20
int main()
{
f<1, s>(); // error: no matching function for call to 'f'
}
=20
int s[] =3D { 1 };
Moving the definition of "s" before main() does compile successfully (aga=
in,
on all three C++ compilers tested), because moving the definition actuall=
y
changes the type of "s". Unlike forwardly-declared types (which have a
consistent type before and after the type is completed), the type of an
array object actually changes once the array's type is completely defined.
That is the significant point that you do not seem to have realized just
yet.
Furthermore, there is no such thing as "compatible" types when it comes t=
o
templates. Granted, a limited number of conversions is allowed for certai=
n
non-type arguments - but none are allowed for reference non-type argument=
s.
Therefore there is no conversion between an incomplete array type and any
bounded array type. Besides, if complete and incomplete array types were
compatible, then the following overloads of f() should be ambiguous:
template <int (&a)[]>
void f() {}
template <int (&a)[1]>
void f() {}
But they are not ambiguous in the least.
> There's not the slightest doubt that the template function
> should be instantiated.
The EDG, gcc, and Comeau C++ compilers show not the slightest doubt that =
a
complete array type cannot be passed as a template nontype argument where=
an
incomplete array type has been declared. In other words, as long as the
program tries to instantiate accept() with a array whose type is complete=
,
the program will not compile successfully.
> The question centers around the notion
> of when the incompleteness is resolved: in the definition of the
> template, or when the template is instantiated. And it is
> linked to a dependant function call---if I call a dependent
> function in a template in which the parameter type is
> incomplete, but the instantiation type is complete, does the
> dependent fonction see the complete type, or only the
> incomplete. (In the case of g++, it only sees the incomplete
> type, but the question seems awkward enough that I want an
> answer based on the standard.)
The answer is that the type of the argument is determined at the point th=
at
the template instantiation appears in the source file - and that is the
reason why moving the definition of the array after that point works - wh=
ile
having the definition appear prior to that point - is certain to fail.
>> In other words, an incomplete array type is a
>> different type than a complete array type:
>=20
> Different, but compatible.
Either the types are the same or they are different. Since they are
different in this case - the types are incompatible.
=20
>> "The declared type of an array object might be an array of unknown siz=
e and
>> therefore be incomplete at one point in a translation unit and complet=
e
>> later on;
>=20
> That is exactly the phrase on which my problem hinges. The type
> is incomplete when the template is defined, but it is complete
> when the template is instantiated.
The accept() function template was defined with an incomplete array type =
and
therefore it can only be instantiated with an array whose type is
incomplete. Accept() may not be instantiated with any array whose type is
complete - because that array object has the wrong type to match the
declaration.
=20
>> the array types at those two points (=B3array of unknown bound of
>> T=B2 and =B3array of N[T]=B2) are different types. The type of a point=
er to array
>> of unknown size, or of a type defined by a typedef declaration to be a=
n
>> array of unknown size, cannot be completed." [=A73.9/7]
>=20
> That is, perhaps, significant. Although it doesn't actually
> mention reference to array, it doesn't take much imagination to
> think it this is also meant.
>=20
>> So in order to instantiate accept() with tab1 and tab2, it is necessar=
y to
>> ensure that their types are complete only after accept() has been
>> instantiated:
>=20
> Which is totally wrong, since the types are compatible.
The program above proves the contrary - otherwise how could moving the
definition of the array object make any difference at all? And once again=
,
there is no such thing as "compatible" types.
> [..]
>> Unfortunately, the program still fails to compile because accept() use=
s its
>> own nontype template argument ("keys" which is an incomplete type) as =
a
>> function parameter in calls to the function templates begin() and end(=
) -
>> both of which use the function parameter's type to deduce a complete a=
rray
>> type - a deduction which naturally fails when the parameter's type is
>> certain to be incomplete.
>=20
> The problem is that accept uses its non-type template argument
> in a context where a complete type is required, yes. There's no
> problem instantiating accept. The problem involves whether
> instantiating it with a complete type results in a complete
> type.
Any attempt to pass a complete array type where an incomplete non-type
parameter is declared will not compile. And if you can produce such a
program that does compile successfully, then by all means post it.
=20
> I'd also be curious if there was a clever work-around which
> worked. A priori, the compiler has all of the knowledge which
> it needs, but I can't figure out a way of making it use it.
The issues are exactly as I have described them: first, passing an array
with a complete type where an array with an incomplete type is required (=
but
fixing that problem leads to the opposite situation) passing an array wit=
h
an incomplete array type where an array with a complete type is needed.
Greg
---
[ 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.comeaucomputing.com/csc/faq.html ]