Topic: Incomplete type in template parameter, complete type as argument


Author: "Vidar Hasfjord" <vattilah-groups@yahoo.co.uk>
Date: Mon, 16 Apr 2007 03:06:19 CST
Raw View
On Apr 11, 5:01 pm, "James Kanze" <james.ka...@gmail.com> wrote:
> Even in C++, the notion of compatible type exists.  Whether it applies
> with regards to incomplete and complete array types, I'm not sure, and
> whether it applies in this case, even less, but the concept is there
> (if only for reasons of C compatibility, in certain cases).

In my view the "complete or incomplete type" question is a red herring
in this case. Answering a simpler question; "On which parameters is
the template parameterized?"; seems to give a simple answer that
confirms the compiler error. (By the way, VC++ 8 complains with error
C2265: "'abstract declarator' : reference to a zero-sized array is
illegal" on the template declaration itself.)

Here is your template again:

  template <char const* (&keys) []>
  bool accept (char const* key) {
    return std::find_if (begin (keys), end (keys), Match (key))
      != end (keys);
  }

This template is simply not parameterized on array size. There is no
rule that says it is implicitly parameterized on size just because the
size is omitted. Hence, this template is only parameterized on
references to a single array type; an array of unspecified size, which
by standard rules decays to a pointer. If you want to parameterize the
template on the size of the array (i.e. a range of array types; since
arrays of different sizes are of different types), you have to say so
explicitly:

  template <size_t N, char const* (&keys) [N]>
  bool accept (char const* key)
  {
    return std::find_if (begin (keys), end (keys), Match (key))
      != end (keys);
  }

> It may be an error in g++ (the fact that g++
> does refuse to instantiate in simpler cases tends to confirm this),
> but the template was definitly being instantiated.

Unlike Visual C++, g++ may allow declaration and instantiation of the
template. I don't know which compiler displays the correct behaviour,
but what's beyond doubt AFAIAC is that all instantiations are using a
single array type, an array-of-unspecified-size type (char const* []).
This type doesn't carry size information, hence the error in the
instantiation (since the implementation depends on that size info).

> > Accept() may not be instantiated with any array whose type is
> > complete - because that array object has the wrong type to match the
> > declaration.
>
> In which case, I'd call that a defect in the standard.

Not by my interpretation. There is a mismatch of types like Greg
Herlihy says. The exact type of the array argument passed to the
template cannot be captured because the template is simply not
parameterized on array type (the size in this case).

> The compiler has the information it needs at the point of
> instantiation.  The problem is how to tease them out of it.

True. It has all the information, but the template as you've declared
it doesn't allow for parameterization on that info (i.e. on the size).
Hence it decays to the parameter type you've specified, an array-of-
unspecified-size type.

Using my declaration above you can implement your table as follows:

  Table const tbl [] =
  {
    {1, &accept <N_ELEMENTS (tab1), tab1>},
    {2, &accept <N_ELEMENTS (tab2), tab2>},
  };

This is a little more to write but otherwise as you intended.

If that is too verbose, you can use functors, provided the rest of
your program can be adapted, of course. A functor allows the array
type, the size in this case, to be captured through a templated
constructor:

  struct Accept
  {
    char const* const* keys;
    const size_t n;

    template <size_t N> Accept (char const* (&k) [N])
      : keys (k), n (N) {}

    bool operator () (char const* key) {
      return std::find_if (keys, keys + n, Match (key))
        != keys + n;
    }
  };

  Table const tbl [] =
  {
    {1, Accept (tab1)},
    {2, Accept (tab2)},
  };

Regards,
Vidar Hasfjord

---
[ 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: "James Kanze" <james.kanze@gmail.com>
Date: Mon, 16 Apr 2007 08:42:29 CST
Raw View
On Apr 16, 11:06 am, "Vidar Hasfjord" <vattilah-gro...@yahoo.co.uk>
wrote:
> On Apr 11, 5:01 pm, "James Kanze" <james.ka...@gmail.com> wrote:

> > Even in C++, the notion of compatible type exists.  Whether it applies
> > with regards to incomplete and complete array types, I'm not sure, and
> > whether it applies in this case, even less, but the concept is there
> > (if only for reasons of C compatibility, in certain cases).

> In my view the "complete or incomplete type" question is a red herring
> in this case. Answering a simpler question; "On which parameters is
> the template parameterized?"; seems to give a simple answer that
> confirms the compiler error.

   [...]

> Here is your template again:
>
>   template <char const* (&keys) []>
>   bool accept (char const* key) {
>     return std::find_if (begin (keys), end (keys), Match (key))
>       != end (keys);
>   }

> This template is simply not parameterized on array size.

True.  As I said, the problem is that the compiler has the
necessary information, but there's no way to tease it out of it.

    [...]
> > > Accept() may not be instantiated with any array whose type is
> > > complete - because that array object has the wrong type to match the
> > > declaration.

> > In which case, I'd call that a defect in the standard.

> Not by my interpretation. There is a mismatch of types like Greg
> Herlihy says. The exact type of the array argument passed to the
> template cannot be captured because the template is simply not
> parameterized on array type (the size in this case).

There are two aspects.  As you've pointed out, the template
isn't parameterized on size, only on the "name" of the array.
It seems a little frustrating, but I can see the logic behind
this.  On the other hand, I think that not being able to use an
array of known size anywhere an array of unknown size is
required *is* a defect.  It doesn't make sense.

> > The compiler has the information it needs at the point of
> > instantiation.  The problem is how to tease them out of it.

> True. It has all the information, but the template as you've declared
> it doesn't allow for parameterization on that info (i.e. on the size).
> Hence it decays to the parameter type you've specified, an array-of-
> unspecified-size type.

> Using my declaration above you can implement your table as follows:

>   Table const tbl [] =
>   {
>     {1, &accept <N_ELEMENTS (tab1), tab1>},
>     {2, &accept <N_ELEMENTS (tab2), tab2>},
>   };

> This is a little more to write but otherwise as you intended.

That is one solution; I'd thought of that.  The other solution
would be to modify the code to use a sentinel (so that the
template function doesn't need the length), forward declare the
arrays, with unknown size, instantiate the templates, and only
then define the arrays.

(In practice, the solution was to go back to the classical
solution, using inheritance instead of templates.  My playing
around with templates here was only an experiment, to see if it
would make sense.)

> If that is too verbose, you can use functors, provided the rest of
> your program can be adapted, of course. A functor allows the array
> type, the size in this case, to be captured through a templated
> constructor:

>   struct Accept
>   {
>     char const* const* keys;
>     const size_t n;

>     template <size_t N> Accept (char const* (&k) [N])
>       : keys (k), n (N) {}

>     bool operator () (char const* key) {
>       return std::find_if (keys, keys + n, Match (key))
>         != keys + n;
>     }
>   };

>   Table const tbl [] =
>   {
>     {1, Accept (tab1)},
>     {2, Accept (tab2)},
>   };

I thought of that.  I just bothers me that the initialization
ceases to be static (not that it matters, in this case---the
functions using the table are never called from the constructor
of a static object).

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ 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: numerical.simulation@web.de
Date: Tue, 17 Apr 2007 11:05:52 CST
Raw View
On 16 Apr., 16:42, "James Kanze" <james.ka...@gmail.com> wrote:
> (In practice, the solution was to go back to the classical
> solution, using inheritance instead of templates.  My playing
> around with templates here was only an experiment, to see if it
> would make sense.)

It makes sense. Only the rules are counter-intuitive in ways that
are no longer to be kept in mind.

What strikes me most is that gcc and KAI C++ _used_ to have intuitive
behaviour and template things worked smoothly a few years ago.
Now after some standard's clarification I find myself back in template
hell
shaking head all the time.

Correct me if I am wrong:
I am not quite sure, but I think this discussion is somehow related to
a passage in
http://www.cs.colorado.edu/~siek/pubs/pubs/2006/siek06_sem_cpp.pdf

<cite>
The semantics of member function instantiation is a point of some
controversy.
Section [14.6.4.1 p1] of the Standard says that the point of
instantiation
for a member function immediately follows the enclosing declaration
that triggered
the instantiation (with a caveat for dependent uses within templates).
The
problem with this rule is that uses of a member function may legally
precede its
definition and the definition is needed to generate the instantiation.
(A use of a
member function must only come after the declaration of the member
function,
which is in the template specialization.) In general, the C++ Standard
is formulated
to allow for compilation in a single pass, whereas the current rules
for
member instantiation would require two passes. Also, there is a
disconnect between
the Standard and the current implementations. The Edison Design Group
and GNU compilers both delay the instantiation of member functions to
the end
of the program (or translation unit). We discussed this issue on C++
committee2
and the opinion was that this is a defect in the C++ Standard and that
instantiation
of member functions should be allowed to occur at the end of the
program.
Therefore, C++.T places instantiations for member functions at the end
of the
program.
</cite>


Best Regards,

Markus

---
[ 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: James Kanze <james.kanze@gmail.com>
Date: Wed, 18 Apr 2007 08:30:17 CST
Raw View
On Apr 17, 7:05 pm, numerical.simulat...@web.de wrote:
> On 16 Apr., 16:42, "James Kanze" <james.ka...@gmail.com> wrote:

> What strikes me most is that gcc and KAI C++ _used_ to have intuitive
> behaviour and template things worked smoothly a few years ago.

You mean before two phased look-up?

> Now after some standard's clarification I find myself back in
> template hell shaking head all the time.

> Correct me if I am wrong:
> I am not quite sure, but I think this discussion is somehow related to
> a passage inhttp://www.cs.colorado.edu/~siek/pubs/pubs/2006/siek06_sem_cpp.pdf

> <cite>
> The semantics of member function instantiation is a point of
> some controversy.  Section [14.6.4.1 p1] of the Standard says
> that the point of instantiation for a member function
> immediately follows the enclosing declaration that triggered
> the instantiation (with a caveat for dependent uses within
> templates).  The problem with this rule is that uses of a
> member function may legally precede its definition and the
> definition is needed to generate the instantiation.  (A use of
> a member function must only come after the declaration of the
> member function, which is in the template specialization.) In
> general, the C++ Standard is formulated to allow for
> compilation in a single pass, whereas the current rules for
> member instantiation would require two passes. Also, there is
> a disconnect between the Standard and the current
> implementations. The Edison Design Group and GNU compilers
> both delay the instantiation of member functions to the end of
> the program (or translation unit). We discussed this issue on
> C++ committee2 and the opinion was that this is a defect in
> the C++ Standard and that instantiation of member functions
> should be allowed to occur at the end of the program.
> Therefore, C++.T places instantiations for member functions at
> the end of the program.
> </cite>

It don't think this is the problem.  The above passage speaks
about the point of instantiation.  What seems to cause the most
problems, however, is the fact that compilers now parse a
significant part of the template when it is defined, before any
instantiation, and at the point where it is defined.  Back in
the good old days(TM), templates acted in many ways like macros;
until they were instantiated, they were just text.  And the
instantiation substituted the name of the actual type for T, or
whatever, and compiled that.  (Of course, they were never quite
this simple.  But compared to today, it often seemed like it.)
It's a very intuitive model.  Also a possibly error prone one; a
template could pick up all sorts of junk its author never
considered.  So we get the present rules, where a template can
only pick up all sorts of junk for a dependent name (and
presumably, that's what the author wanted).

The goal, in itself, is laudable.  Whether it is worth the
price, or whether the best solution was chosen, is an open
question, I think.

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ 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: "James Kanze" <james.kanze@gmail.com>
Date: Wed, 11 Apr 2007 10:01:48 CST
Raw View
On Apr 9, 3:35 am, gre...@pacbell.net (Greg Herlihy) wrote:
> On 4/8/07 1:17 PM, in article
> 1176052237.259633.230...@o5g2000hsb.googlegroups.com, "James Kanze"
> <james.ka...@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"

> >> <james.ka...@gmail.com> wrote:
> >> The tbl array attempts to instantiate the accept() function
> >> template with two bounded arrays types (meaning that in
> >> each instance the nontype argument is a complete type). The
> >> attempt to instantiate accept() fails (and fails correctly)
> >> because the nontype argument used to instantiate accept()
> >> in both cases is not the same type as the type specified in
> >> accept's function template declaration.

> > 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 array type

You mean we've broken something that fundamental?  It certainly
is in C, and IIRC, something like the following worked in
CFront:

    void f( int (&array)[] ) ;

    void g()
    {
        int a[ 3 ] ;
        f( a ) ;
    }

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.)

> - they are distinct and completely different types - as the following
> program demonstrates (confirmed with gcc, Comeau and EDG):

>     template <int N, int (&a)[N]>
>     void f() {}

>     extern int s[];

>     int main()
>     {
>         f<1, s>(); // error: no matching function for call to 'f'
>     }

>     int s[] = { 1 };

That I can understand.  You can't use an incomplete type where a
complete type is required.  The reverse should work.

> Moving the definition of "s" before main() does compile successfully (again,
> on all three C++ compilers tested), because moving the definition actually
> 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.

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++.)

> Furthermore, there is no such thing as "compatible" types when it comes to
> templates.

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.

> Granted, a limited number of conversions is allowed for certain
> non-type arguments - but none are allowed for reference non-type arguments.
> 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.

That's an interesting point.  I would argue that one is a better
match... matching the exact type is a better match than matching
a "compatible" type.  But I don't know that the standard
actually considers this case.

> > 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.

G++ seems to agree in simple cases, but not when the code
becomes more complicated (as in my example).  In my example, it
complained about not finding a match for begin() and end()
(which means that it must be instantiating the function, since
these are dependent function calls).

> 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.

If I replace the call to std::find_if (and the begin and end's),
and use a sentinal, it does compile.

> > 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 that
> the template instantiation appears in the source file - and that is the
> reason why moving the definition of the array after that point works - while
> 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:

> > Different, but compatible.

> Either the types are the same or they are different. Since they are
> different in this case - the types are incompatible.

Even in C++, the notion of compatible type exists.  Whether it
applies with regards to incomplete and complete array types, I'm
not sure, and whether it applies in this case, even less, but
the concept is there (if only for reasons of C compatibility, in
certain cases).

> >> "The declared type of an array object might be an array of unknown size and
> >> therefore be incomplete at one point in a translation unit and complete
> >> later on;

> > 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.

Which is why I was getting error messages from the
instantiations?  It may be an error in g++ (the fact that g++
does refuse to instantiate in simpler cases tends to confirm
this), but the template was definitly being instantiated.

> Accept() may not be instantiated with any array whose type is
> complete - because that array object has the wrong type to match the
> declaration.

In which case, I'd call that a defect in the standard.

> >     [..]
> >> Unfortunately, the program still fails to compile because accept() uses 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 array
> >> type - a deduction which naturally fails when the parameter's type is
> >> certain to be incomplete.

> > 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.

Regretfully, I didn't safe to modification using sentinals which
did compile.  Maybe I did do something different (e.g. declare a
pointer to the array, and not a reference), since I can't
reproduce the case.  (I've since rewritten the actual code to
use the classical solution, based on inheritance rather than
templates.)

> > 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 with
> an incomplete array type where an array with a complete type is needed.

The compiler has the information it needs at the point of
instantiation.  The problem is how to tease them out of it.

--
James Kanze (GABI Software)             email:james.kanze@gmail.com
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ 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: "James Kanze" <james.kanze@gmail.com>
Date: Fri, 6 Apr 2007 09:59:04 CST
Raw View
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:

    template< typename T, size_t N >
    T*
    begin( T (&array)[ N ] )
    {
        return array ;
    }

    template< typename T, size_t N >
    T*
    end( T (&array)[ N ] )
    {
        return array + N ;
    }

    struct Match
    {
        explicit            Match( char const* key )
            :   myKey( key )
        {
        }
        bool                operator()( char const* lhs ) const
        {
            return strcmp( lhs, myKey ) == 0 ;
        }
        char const*         myKey ;
    } ;

    template< char const* (&keys)[] >
    bool
    accept(
        char const*         key )
    {
        return std::find_if( begin( keys ), end( keys ),
Match( key ) )
            != end( keys ) ;
    }

    char const*         tab1[] =
    {
        "Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Nl", "No"
    } ;

    char const*         tab2[] =
    {
        "Lu", "Ll", "Lt", "Lm", "Lo"
    } ;

    struct Table
    {
        int                 id ;
        bool (*             fnc)( char const* key ) ;
    } ;

    Table const         tbl[] =
    {
        { 1, &accept< tab1 > },
        { 2, &accept< tab2 > },
    } ;

G++ complains because, if I interpret the error messages
correctly, it still consideres the type of keys as incomplete in
the instantiations of "accept" (which causes problems when it
attempts to instantiate "begin" and "end"), although the
instantiation arguments have complete types.  (The code works if
the template parameters of "accept" are "template< size_t N,
char const* (&keys)[ N ] >", and I explicitly pass the length of
the array.)  On one hand, I can see its point, but on the
other... if it really wants to complain, it shouldn't allow me
to instantiate the template with a complete type either:-).  It
is the nature of incomplete types to be completed.

FWIW: I can't find anything in the standard concerning the
handling of incomplete types in template non-type parameters.
G++ also complains about:

    struct T ;

    template< T const* p >
    bool
    f()
    {
        return p->ok() ;
    }

    struct T
    {
        bool ok() const { return true ; }
    } ;

    T anA ;

    bool (*pf)() = &f< &anA > ;

Which really seems like it should be legal.

--
James Kanze (Gabi Software)            email: james.kanze@gmail.com
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ 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: "James Kanze" <james.kanze@gmail.com>
Date: Sun, 8 Apr 2007 14:17:35 CST
Raw View
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"

> <james.ka...@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:

> >     template< char const* (&keys)[] >
> >     bool
> >     accept(
> >         char const*         key )
> >     {
> >         return std::find_if( begin( keys ), end( keys ),
> > Match( key ) )
> >             != end( keys ) ;
> >     }

> As noted, the template function keys(), specifies an incomplete type (an
> unbouned array type) as its nontype template parameter.

Yes.  That is, of course, the crux of my question.

> >     char const*         tab1[] =
> >     {
> >         "Lu", "Ll", "Lt", "Lm", "Lo", "Nd", "Nl", "No"
> >     } ;

> >     char const*         tab2[] =
> >     {
> >         "Lu", "Ll", "Lt", "Lm", "Lo"
> >     } ;

> >     struct Table
> >     {
> >         int                 id ;
> >         bool (*             fnc)( char const* key ) ;
> >     } ;

> >     Table const         tbl[] =
> >     {
> >         { 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 argument
> is a complete type). The attempt to instantiate accept() fails (and fails
> correctly) because the nontype argument used to instantiate accept() in both
> cases is not the same type as the type specified in accept's function
> template declaration.

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* [].

There's not the slightest doubt that the template function
should be instantiated.  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.)

> In other words, an incomplete array type is a
> different type than a complete array type:

Different, but compatible.

> "The declared type of an array object might be an array of unknown size and
> therefore be incomplete at one point in a translation unit and complete
> later on;

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 array types at those two points (   array of unknown bound of
> T    and    array of N[T]   ) 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." [   3.9/7]

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.

> So in order to instantiate accept() with tab1 and tab2, it is necessary to
> ensure that their types are complete only after accept() has been
> instantiated:

Which is totally wrong, since the types are compatible.

    [..]
> Unfortunately, the program still fails to compile because accept() uses 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 array
> type - a deduction which naturally fails when the parameter's type is
> certain to be incomplete.

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.

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.

--
James Kanze (Gabi Software)            email: james.kanze@gmail.com
Conseils en informatique orient   e objet/
                   Beratung in objektorientierter Datenverarbeitung
9 place S   mard, 78210 St.-Cyr-l'   cole, France, +33 (0)1 30 23 00 34


---
[ 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: cbarron3@ix.netcom.com (Carl Barron)
Date: Mon, 9 Apr 2007 15:01:49 GMT
Raw View
James Kanze <james.kanze@gmail.com> wrote:

>
> 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.

how about a set of structs of the same form with the same static
members.  Then instead of begin(),end(),and accept depending on a non
typed data they depend on one of these structs of static members.

struct table_a
{
  static char **data;
  static const int size;
};

struct table_b
{
   static char const **data;
   static const int size;
};

template <class T>
char const **begin() {return T::data;}

template <class T>
char const **end() {return T::data + T::size;}

template <class T>
bool accept(char const *key)
{
  return std::find(begin<T>(),end<T>(),key) != end<T>();
}

struct Table
{
        int id;
        bool (*fnc)(char const *)
};

Table const tbl[] = ...;

---
[ 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                      ]