gcc-4.4.3 failure in rejecting instantiation?

Discussion in 'C++' started by Paul Bibbings, Jun 19, 2010.

  1. I've been working on some code (not mine) that relies on the following
    motif leading to a compilation failure, with sensible output, at line
    17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:

    template<typename T>
    struct Error { };

    template<typename T>
    struct A
    {
    static const int i = Error<T>::TempArgMustBeInt; // line: 7
    };

    template<>
    struct A<int>
    { };

    int main()
    {
    A<int> A_int;
    A<char> A_char; // line: 17
    }

    I'm thinking that it probably should (or, I have no immediate
    understanding of why it wouldn't); whoever wrote the original code
    (probably some while ago) thought it should; and, Comeau seems to
    agree:

    Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
    ONLINE_EVALUATION_BETA2
    Copyright 1988-2008 Comeau Computing. All rights reserved.
    MODE:strict errors C++ noC++0x_extensions

    "ComeauTest.c", line 7: error: class "Error<char>" has no member
    "TempArgMustBeInt"
    static const int i = Error<T>::TempArgMustBeInt;
    ^
    detected during instantiation of class "A<T> [with T=char]" at
    line 17
    // ...

    1 error detected in the compilation of "ComeauTest.c".

    What does anyone else think?

    Regards

    Paul Bibbings
     
    Paul Bibbings, Jun 19, 2010
    #1
    1. Advertising

  2. * Paul Bibbings, on 20.06.2010 00:15:
    >
    > I've been working on some code (not mine) that relies on the following
    > motif leading to a compilation failure, with sensible output, at line
    > 17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:
    >
    > template<typename T>
    > struct Error { };
    >
    > template<typename T>
    > struct A
    > {
    > static const int i = Error<T>::TempArgMustBeInt; // line: 7
    > };
    >
    > template<>
    > struct A<int>
    > { };
    >
    > int main()
    > {
    > A<int> A_int;
    > A<char> A_char; // line: 17
    > }
    >
    > I'm thinking that it probably should (or, I have no immediate
    > understanding of why it wouldn't); whoever wrote the original code
    > (probably some while ago) thought it should; and, Comeau seems to
    > agree:
    >
    > Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
    > ONLINE_EVALUATION_BETA2
    > Copyright 1988-2008 Comeau Computing. All rights reserved.
    > MODE:strict errors C++ noC++0x_extensions
    >
    > "ComeauTest.c", line 7: error: class "Error<char>" has no member
    > "TempArgMustBeInt"
    > static const int i = Error<T>::TempArgMustBeInt;
    > ^
    > detected during instantiation of class "A<T> [with T=char]" at
    > line 17
    > // ...
    >
    > 1 error detected in the compilation of "ComeauTest.c".
    >
    > What does anyone else think?


    Sounds like it incorrectly optimizes the "i" away (perhaps it optimizes A_char
    away) before checking whether it exists.

    I'd try a typedef instead.

    I'm assuming that there is some good reason for the restriction (e.g., A used as
    a template template parameter); otherwise just remove the templating... :)


    Cheers & hth.,

    - Alf


    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach /Usenet, Jun 20, 2010
    #2
    1. Advertising

  3. "Alf P. Steinbach /Usenet" <> writes:

    > * Paul Bibbings, on 20.06.2010 00:15:
    >>
    >> I've been working on some code (not mine) that relies on the following
    >> motif leading to a compilation failure, with sensible output, at line
    >> 17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:
    >>
    >> template<typename T>
    >> struct Error { };
    >>
    >> template<typename T>
    >> struct A
    >> {
    >> static const int i = Error<T>::TempArgMustBeInt; // line: 7
    >> };
    >>
    >> template<>
    >> struct A<int>
    >> { };
    >>
    >> int main()
    >> {
    >> A<int> A_int;
    >> A<char> A_char; // line: 17
    >> }
    >>
    >> I'm thinking that it probably should (or, I have no immediate
    >> understanding of why it wouldn't); whoever wrote the original code
    >> (probably some while ago) thought it should; and, Comeau seems to
    >> agree:
    >>
    >> Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
    >> ONLINE_EVALUATION_BETA2
    >> Copyright 1988-2008 Comeau Computing. All rights reserved.
    >> MODE:strict errors C++ noC++0x_extensions
    >>
    >> "ComeauTest.c", line 7: error: class "Error<char>" has no member
    >> "TempArgMustBeInt"
    >> static const int i = Error<T>::TempArgMustBeInt;
    >> ^
    >> detected during instantiation of class "A<T> [with T=char]" at
    >> line 17
    >> // ...
    >>
    >> 1 error detected in the compilation of "ComeauTest.c".
    >>
    >> What does anyone else think?

    >
    > Sounds like it incorrectly optimizes the "i" away (perhaps it
    > optimizes A_char away) before checking whether it exists.
    >
    > I'd try a typedef instead.
    >
    > I'm assuming that there is some good reason for the restriction (e.g.,
    > A used as a template template parameter); otherwise just remove the
    > templating... :)


    The code that I was looking at, and which used the above motif, is from
    a paper by McNamara and Smaragdakis on "Static interfaces in C++" -
    http://www.cc.gatech.edu/~yannis/static-interfaces/static-interfaces.pdf
    (I haven't found the date for this paper, but the latest reference is
    Alexandrescu, 2000). The example illustrates static dispatch, along the
    lines of:

    enum {
    SET_HASH, // implement using a hash table
    SET_BST, // implement using a binary search tree
    SET_LL, // implement using a linked list
    SET_NONE // no appropriate implementation
    };

    template<class T>
    struct SetDispatch {
    static const bool Hash =
    StaticIsA<T, Hashtable>::valid;
    static const bool LtC =
    StaticIsA<T, LessThanComparable<T> >::valid;
    static const bool EqT =
    StaticIsA<T, EqualityComparable<T> >::valid;
    static const int which = Hash ? SET_HASH
    : LtC ? SET_BST
    : EqT ? SET_LL
    : SET_NONE;
    };

    template<
    class T,
    int which = SetDispatch<T>::which
    >

    struct Set;

    template<class T>
    struct Set<T, SET_NONE> {
    static const int x = Error<T>::
    Set_only_works_on_Hashtables_or_LTCs_or_ECs;
    };

    template<class T>
    struct Set<T, SET_LL> { /* ... */ };

    template<class T>
    struct Set<T, SET_BST> { /* ... */ };

    template<class T>
    struct Set<T, SET_HASH> { /* ... */ };

    By default the implementation does not accept fundamental types as
    LessThanComparable, EqualityComparable, etc., and this is achieved with
    appropraite specializations of a local traits type:

    struct Valid {
    static const bool valid = true;
    protected:
    ~Valid() { }
    };

    template<>
    template<>
    struct LessThanComparable<int>::Traits<int> : public Valid { };

    Without any such specializations, instantiations such as:

    Set<char> char_set;

    should fail with a nice (4-line) error output finishing with:

    error: `Set_only_works_on_Hashtables_or_LTCs_or_ECs' is not a member
    of `Error<char>'

    with the advantage that the failure:

    1. signals what is `missing' for the particular type as template
    argument;
    2. signals the type that failed as template argument (the reason for
    parameterizing Error); and
    3. gives concise error output that doesn't go deep into the bowels of
    the implementation.

    However, *without* the traits specialization for char, the
    implementation *allows*:

    Set<char> char_set;

    To get the code working I can replace:

    template<class T>
    struct Set<T, SET_NONE> {
    static const int x = Error<T>::
    Set_only_works_on_Hashtables_or_LTCs_or_ECs;
    };

    with:

    template<class T>
    struct Set<T, SET_NONE> {
    Set() {
    const int x = Error<T>::
    Set_only_works_on_Hashtables_or_LTCs_or_ECs;
    }
    };

    but the effect is not necessarily the same in all cases (or, at least, I
    would have to think it through to decide whether it is).

    Regards

    Paul Bibbings
     
    Paul Bibbings, Jun 20, 2010
    #3
  4. Paavo Helde <> writes:

    > Paul Bibbings <> wrote in
    > news::
    >
    >>
    >> I've been working on some code (not mine) that relies on the following
    >> motif leading to a compilation failure, with sensible output, at line
    >> 17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:
    >>
    >> template<typename T>
    >> struct Error { };
    >>
    >> template<typename T>
    >> struct A
    >> {
    >> static const int i = Error<T>::TempArgMustBeInt; // line: 7
    >> };
    >>
    >> template<>
    >> struct A<int>
    >> { };
    >>
    >> int main()
    >> {
    >> A<int> A_int;
    >> A<char> A_char; // line: 17
    >> }
    >>
    >> I'm thinking that it probably should (or, I have no immediate
    >> understanding of why it wouldn't); whoever wrote the original code
    >> (probably some while ago) thought it should; and, Comeau seems to
    >> agree:
    >>
    >> Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
    >> ONLINE_EVALUATION_BETA2
    >> Copyright 1988-2008 Comeau Computing. All rights reserved.
    >> MODE:strict errors C++ noC++0x_extensions
    >>
    >> "ComeauTest.c", line 7: error: class "Error<char>" has no member
    >> "TempArgMustBeInt"
    >> static const int i = Error<T>::TempArgMustBeInt;
    >> ^
    >> detected during instantiation of class "A<T> [with T=char]"

    > at
    >> line 17
    >> // ...
    >>
    >> 1 error detected in the compilation of "ComeauTest.c".
    >>
    >> What does anyone else think?

    >
    > This boils down to the question if static data members of a template
    > class will be instantiated and initialized if they are never used.
    >
    > The 2003 standard says in 14.7.1:"... in particular, the initialization
    > (and any associated side-effects) of a static data member does not occur
    > unless the static data member is itself used in a way that requires the
    > definition of the static data member to exist."
    >
    > This would suggest that gcc 4.4 is right and Comeau wrong, but this would
    > be a bit surprising. Probably I have overlooked something.
    >
    > The fix for the code to get it working (i.e. failing ;-) as intended in
    > gcc 4.4 would be easy though, the static data member should be accessed
    > somehow, triggering its instantiation (and a cleanly compiling variant of
    > it to be added to A<int>).


    So, one fix (not necessarily the best) might be to augment the primary
    template for struct A (above) as?:

    template<typename T>
    struct A
    {
    A() { &A::i; }
    static const int i = Error<T>::TempArgMustBeInt;
    };

    As to your reference to [temp.inst] §14.7.1/1, I can't immediately see
    that you have overlooked anything, and I would agree with your
    (tentative) assessment of gcc being "right". (The code that I have
    extracted the example from occurs in a paper written, certainly, after
    2000, and I note that the cited subclause did not change from C++98 to
    C++03, although compilers might have implemented it incorrectly at the
    time.)

    As to Comeau being wrong, however, I don't think it's as straightforward
    to say that. As stated on the `tryitout' start page, Comeau
    instantiates templates at link time and, of course, the online compiler
    doesn't link. This is the reason for the online compiler defaulting to
    the -tused option to force some instantiations, and this is how the
    above code was given to Comeau. But not knowing how the online compiler
    handles compilation that requires instantiation even without the -tused
    flag, it is hard to comment on the fact that it *still* fails even with
    -tused turned off.

    Does anyone here perhaps have a local installation of Comeau that they
    could try the above code with - compile and link?

    Regards

    Paul Bibbings
     
    Paul Bibbings, Jun 20, 2010
    #4
  5. Paul Bibbings <> writes:

    > The code that I was looking at, and which used the above motif, is from
    > a paper by McNamara and Smaragdakis on "Static interfaces in C++" -
    > http://www.cc.gatech.edu/~yannis/static-interfaces/static-interfaces.pdf
    > (I haven't found the date for this paper, but the latest reference is
    > Alexandrescu, 2000).


    Brian McNamara and Yannis Smaragdakis, ¡°Static Interfaces in C++¡±, in
    the C++ Template Programming Workshop, Erfurt, Germany, October
    2000.

    Regards

    Paul Bibbings
     
    Paul Bibbings, Jun 20, 2010
    #5
  6. * Paul Bibbings, on 20.06.2010 12:04:
    > "Alf P. Steinbach /Usenet"<> writes:
    >
    >> * Paul Bibbings, on 20.06.2010 00:15:
    >>>
    >>> I've been working on some code (not mine) that relies on the following
    >>> motif leading to a compilation failure, with sensible output, at line
    >>> 17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:
    >>>
    >>> template<typename T>
    >>> struct Error { };
    >>>
    >>> template<typename T>
    >>> struct A
    >>> {
    >>> static const int i = Error<T>::TempArgMustBeInt; // line: 7
    >>> };
    >>>
    >>> template<>
    >>> struct A<int>
    >>> { };
    >>>
    >>> int main()
    >>> {
    >>> A<int> A_int;
    >>> A<char> A_char; // line: 17
    >>> }
    >>>
    >>> I'm thinking that it probably should (or, I have no immediate
    >>> understanding of why it wouldn't); whoever wrote the original code
    >>> (probably some while ago) thought it should; and, Comeau seems to
    >>> agree:
    >>>
    >>> Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
    >>> ONLINE_EVALUATION_BETA2
    >>> Copyright 1988-2008 Comeau Computing. All rights reserved.
    >>> MODE:strict errors C++ noC++0x_extensions
    >>>
    >>> "ComeauTest.c", line 7: error: class "Error<char>" has no member
    >>> "TempArgMustBeInt"
    >>> static const int i = Error<T>::TempArgMustBeInt;
    >>> ^
    >>> detected during instantiation of class "A<T> [with T=char]" at
    >>> line 17
    >>> // ...
    >>>
    >>> 1 error detected in the compilation of "ComeauTest.c".
    >>>
    >>> What does anyone else think?

    >>
    >> Sounds like it incorrectly optimizes the "i" away (perhaps it
    >> optimizes A_char away) before checking whether it exists.
    >>
    >> I'd try a typedef instead.
    >>
    >> I'm assuming that there is some good reason for the restriction (e.g.,
    >> A used as a template template parameter); otherwise just remove the
    >> templating... :)

    >
    > The code that I was looking at, and which used the above motif, is from
    > a paper by McNamara and Smaragdakis on "Static interfaces in C++" -
    > http://www.cc.gatech.edu/~yannis/static-interfaces/static-interfaces.pdf
    > (I haven't found the date for this paper, but the latest reference is
    > Alexandrescu, 2000). The example illustrates static dispatch, along the
    > lines of:
    >
    > enum {
    > SET_HASH, // implement using a hash table
    > SET_BST, // implement using a binary search tree
    > SET_LL, // implement using a linked list
    > SET_NONE // no appropriate implementation
    > };
    >
    > template<class T>
    > struct SetDispatch {
    > static const bool Hash =
    > StaticIsA<T, Hashtable>::valid;
    > static const bool LtC =
    > StaticIsA<T, LessThanComparable<T> >::valid;
    > static const bool EqT =
    > StaticIsA<T, EqualityComparable<T> >::valid;
    > static const int which = Hash ? SET_HASH
    > : LtC ? SET_BST
    > : EqT ? SET_LL
    > : SET_NONE;
    > };
    >
    > template<
    > class T,
    > int which = SetDispatch<T>::which
    > >

    > struct Set;
    >
    > template<class T>
    > struct Set<T, SET_NONE> {
    > static const int x = Error<T>::
    > Set_only_works_on_Hashtables_or_LTCs_or_ECs;


    Try replacing the constant with

    typedef typename Error<T>::Set_only_works_on_what_it_works_on Oops;

    Disclaimer: I haven't tried this.

    But I can imagine that it would work.


    Cheers & hth.,

    - Alf

    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach /Usenet, Jun 20, 2010
    #6
  7. * Paul Bibbings, on 20.06.2010 12:36:
    > Paavo Helde<> writes:
    >
    >> Paul Bibbings<> wrote in
    >> news::
    >>
    >>>
    >>> I've been working on some code (not mine) that relies on the following
    >>> motif leading to a compilation failure, with sensible output, at line
    >>> 17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:
    >>>
    >>> template<typename T>
    >>> struct Error { };
    >>>
    >>> template<typename T>
    >>> struct A
    >>> {
    >>> static const int i = Error<T>::TempArgMustBeInt; // line: 7
    >>> };
    >>>
    >>> template<>
    >>> struct A<int>
    >>> { };
    >>>
    >>> int main()
    >>> {
    >>> A<int> A_int;
    >>> A<char> A_char; // line: 17
    >>> }
    >>>
    >>> I'm thinking that it probably should (or, I have no immediate
    >>> understanding of why it wouldn't); whoever wrote the original code
    >>> (probably some while ago) thought it should; and, Comeau seems to
    >>> agree:
    >>>
    >>> Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
    >>> ONLINE_EVALUATION_BETA2
    >>> Copyright 1988-2008 Comeau Computing. All rights reserved.
    >>> MODE:strict errors C++ noC++0x_extensions
    >>>
    >>> "ComeauTest.c", line 7: error: class "Error<char>" has no member
    >>> "TempArgMustBeInt"
    >>> static const int i = Error<T>::TempArgMustBeInt;
    >>> ^
    >>> detected during instantiation of class "A<T> [with T=char]"

    >> at
    >>> line 17
    >>> // ...
    >>>
    >>> 1 error detected in the compilation of "ComeauTest.c".
    >>>
    >>> What does anyone else think?

    >>
    >> This boils down to the question if static data members of a template
    >> class will be instantiated and initialized if they are never used.
    >>
    >> The 2003 standard says in 14.7.1:"... in particular, the initialization
    >> (and any associated side-effects) of a static data member does not occur
    >> unless the static data member is itself used in a way that requires the
    >> definition of the static data member to exist."
    >>
    >> This would suggest that gcc 4.4 is right and Comeau wrong, but this would
    >> be a bit surprising. Probably I have overlooked something.
    >>

    [snip]
    >
    > As to your reference to [temp.inst] §14.7.1/1, I can't immediately see
    > that you have overlooked anything, and I would agree with your
    > (tentative) assessment of gcc being "right".


    Consider the following program (off the cuff):

    struct S
    {
    static int const v = 42;
    };

    #include <iostream>
    int main()
    { std::cout << S::v << std::endl; }

    Here the definition of S::v is not required to exist.

    And in fact there is no definition of S::v, just a pure declaration. :)

    It may look as if there is a definition of S::v, but it really is just a pure
    declaration.

    A definition would be, at namespace scope,

    int const S::v;

    which might look like a pure declaration, but is really a definition.


    Cheers,

    - Alf

    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach /Usenet, Jun 20, 2010
    #7
  8. Paul Bibbings

    Öö Tiib Guest

    On 20 juuni, 01:15, Paul Bibbings <> wrote:
    > I've been working on some code (not mine) that relies on the following
    > motif leading to a compilation failure, with sensible output, at line
    > 17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:
    >
    >    template<typename T>
    >    struct Error { };
    >
    >    template<typename T>
    >    struct A
    >    {
    >       static const int i = Error<T>::TempArgMustBeInt;  // line: 7
    >    };
    >
    >    template<>
    >    struct A<int>
    >    { };
    >
    >    int main()
    >    {
    >       A<int>  A_int;
    >       A<char> A_char;                                   // line: 17
    >    }
    >
    > I'm thinking that it probably should (or, I have no immediate
    > understanding of why it wouldn't); whoever wrote the original code
    > (probably some while ago) thought it should; and, Comeau seems to
    > agree:
    >
    >    Comeau C/C++ 4.3.10.1 (Oct  6 2008 11:28:09) for
    >    ONLINE_EVALUATION_BETA2
    >    Copyright 1988-2008 Comeau Computing.  All rights reserved.
    >    MODE:strict errors C++ noC++0x_extensions
    >
    >    "ComeauTest.c", line 7: error: class "Error<char>" has no member
    >      "TempArgMustBeInt"
    >      static const int i = Error<T>::TempArgMustBeInt;
    >                                     ^
    >           detected during instantiation of class "A<T> [with T=char]" at
    >           line 17
    >    // ...
    >
    >    1 error detected in the compilation of "ComeauTest.c".
    >
    > What does anyone else think?


    What we have? For bare human eye it looks wrong. Comeau issues a
    diagnostic and gcc does not issue one.

    It is probably one of the "program is ill-formed but no diagnostic is
    required" cases or undefined by standard cases. I can not imagine that
    standard is saying somewhere it is well-formed program.

    The feature of declaring static integral constant values in class
    declaration is sort of exceptional (so something about it might be is
    left undefined). Standard talking about templates contains "no
    diagnostic required" rather often so it may be one from there.

    As for 2000, people were trying every sort of possibilities back then
    how to use templates and macros for meta-programming. Resulting
    compiler messages were terrible to read so people also investigated
    how to improve compile-time error checking. Casual black magic in a
    book is perhaps not that uncommon because some tricks used and also
    published were found by trial and error.
     
    Öö Tiib, Jun 20, 2010
    #8
  9. "Alf P. Steinbach /Usenet" <> writes:

    > * Paul Bibbings, on 20.06.2010 12:04:
    >> "Alf P. Steinbach /Usenet"<> writes:
    >>
    >>> * Paul Bibbings, on 20.06.2010 00:15:
    >>>>
    >>>> I've been working on some code (not mine) that relies on the following
    >>>> motif leading to a compilation failure, with sensible output, at line
    >>>> 17 (below). The trouble is, with gcc-4.4.3, it *doesn't* fail:
    >>>>
    >>>> template<typename T>
    >>>> struct Error { };
    >>>>
    >>>> template<typename T>
    >>>> struct A
    >>>> {
    >>>> static const int i = Error<T>::TempArgMustBeInt; // line: 7
    >>>> };
    >>>>
    >>>> template<>
    >>>> struct A<int>
    >>>> { };
    >>>>
    >>>> int main()
    >>>> {
    >>>> A<int> A_int;
    >>>> A<char> A_char; // line: 17
    >>>> }
    >>>>
    >>>> I'm thinking that it probably should (or, I have no immediate
    >>>> understanding of why it wouldn't); whoever wrote the original code
    >>>> (probably some while ago) thought it should; and, Comeau seems to
    >>>> agree:
    >>>>
    >>>> Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
    >>>> ONLINE_EVALUATION_BETA2
    >>>> Copyright 1988-2008 Comeau Computing. All rights reserved.
    >>>> MODE:strict errors C++ noC++0x_extensions
    >>>>
    >>>> "ComeauTest.c", line 7: error: class "Error<char>" has no member
    >>>> "TempArgMustBeInt"
    >>>> static const int i = Error<T>::TempArgMustBeInt;
    >>>> ^
    >>>> detected during instantiation of class "A<T> [with T=char]" at
    >>>> line 17
    >>>> // ...
    >>>>
    >>>> 1 error detected in the compilation of "ComeauTest.c".
    >>>>
    >>>> What does anyone else think?
    >>>
    >>> Sounds like it incorrectly optimizes the "i" away (perhaps it
    >>> optimizes A_char away) before checking whether it exists.
    >>>
    >>> I'd try a typedef instead.
    >>>
    >>> I'm assuming that there is some good reason for the restriction (e.g.,
    >>> A used as a template template parameter); otherwise just remove the
    >>> templating... :)

    >>
    >> The code that I was looking at, and which used the above motif, is from
    >> a paper by McNamara and Smaragdakis on "Static interfaces in C++" -
    >> http://www.cc.gatech.edu/~yannis/static-interfaces/static-interfaces.pdf
    >> (I haven't found the date for this paper, but the latest reference is
    >> Alexandrescu, 2000). The example illustrates static dispatch, along the
    >> lines of:
    >>
    >> enum {
    >> SET_HASH, // implement using a hash table
    >> SET_BST, // implement using a binary search tree
    >> SET_LL, // implement using a linked list
    >> SET_NONE // no appropriate implementation
    >> };
    >>
    >> template<class T>
    >> struct SetDispatch {
    >> static const bool Hash =
    >> StaticIsA<T, Hashtable>::valid;
    >> static const bool LtC =
    >> StaticIsA<T, LessThanComparable<T> >::valid;
    >> static const bool EqT =
    >> StaticIsA<T, EqualityComparable<T> >::valid;
    >> static const int which = Hash ? SET_HASH
    >> : LtC ? SET_BST
    >> : EqT ? SET_LL
    >> : SET_NONE;
    >> };
    >>
    >> template<
    >> class T,
    >> int which = SetDispatch<T>::which
    >> >

    >> struct Set;
    >>
    >> template<class T>
    >> struct Set<T, SET_NONE> {
    >> static const int x = Error<T>::
    >> Set_only_works_on_Hashtables_or_LTCs_or_ECs;

    >
    > Try replacing the constant with
    >
    > typedef typename Error<T>::Set_only_works_on_what_it_works_on Oops;
    >
    > Disclaimer: I haven't tried this.
    >
    > But I can imagine that it would work.


    To go back to the original example code (top), if I replace the primary
    template with:

    template<typename T>
    struct A
    {
    typedef typename Error<T>::TempArgMustBeInt type;
    };

    then everything works nicely and, of course, this is the `conventional'
    (modern) way of doing this kind of thing.

    14:42:48 Paul Bibbings@JIJOU
    /cygdrive/d/CPPProjects/ $gcc -c test.cpp
    test_.cpp: In instantiation of ¡®A<char>¡¯:
    test.cpp:19: instantiated from here
    test.cpp:9: error: no type named ¡®TempArgMustBeInt¡¯ in ¡®struct
    Error<char>¡¯

    The same method can, then - as you say - be applied in (adapted to) the
    code from McNamara and Smaragdakis, I'm sure.

    Regards

    Paul Bibbings
     
    Paul Bibbings, Jun 20, 2010
    #9
  10. "Alf P. Steinbach /Usenet" <> writes:

    > Consider the following program (off the cuff):
    >
    > struct S
    > {
    > static int const v = 42;
    > };
    >
    > #include <iostream>
    > int main()
    > { std::cout << S::v << std::endl; }
    >
    > Here the definition of S::v is not required to exist.
    >
    > And in fact there is no definition of S::v, just a pure declaration. :)
    >
    > It may look as if there is a definition of S::v, but it really is just
    > a pure declaration.
    >
    > A definition would be, at namespace scope,
    >
    > int const S::v;
    >
    > which might look like a pure declaration, but is really a definition.


    The reference for this is [class.static.data] §9.4.2/4

    "If a static data member is of const integral or const enumeration
    type, its declaration in the class definition can specify a
    constant-initializer which shall be an integral constant expression
    (5.19). In that case, the member can appear in integral constant
    expressions. The member shall still be defined in a namespace scope
    if it is used in the program and the namespace scope definition shall
    not contain an initializer."

    But, from my reading of this subclause, I don't feel certain that the
    above example - which gcc is happy with - is correct.

    The reason for my doubt stems from [basic.def.odr] §3.2/2:

    "An expression is /potentially evaluated/ unless it appears where an
    integral constant expression is required... An object or
    non-overloaded function is /used/ if its name appears in a
    potentially-evaluated expression. ..."

    What this means, in effect - if I am reading it correctly - is that
    "appear[ance] where an integral constant expression is required" doesn't
    constitute a "use." Therefore the last sentence of §9.4.2/4 doesn't
    apply in this case, and the following is valid, IIUC:

    struct S
    {
    static const int v = 42;
    static const int w = v - 1; // #1
    }

    In this case, in line #1, v "appears where an integral constant
    expression is required" and thus a definition (according to §9.4.2/4) is
    *not* required.

    However, since (from your code), the call:

    std::cout << S::v << std::endl;

    invokes:

    std::basic_ostream<char, std::char_traits<char> >&
    std::eek:stream::eek:perator<<(int);

    I can't see that we have the same situation here. Matching the int
    parameter to the call to std::eek:stream::eek:p<< does *not*, in my view,
    constitute a context "where an integral constant expression is
    required." This seems clear from the fact that:

    int i = 42;
    std::cout << i << std::endl; // #2

    works, where:

    template<int N> // requires const. integral expr. as argument
    struct A { };
    A<i> a;

    does not. Therefore, the expression `i' (in #2) seems to constitute a
    `use' according to §3.2/2 and thereby invokes "The member shall still
    be defined in a namespace scope if it is *used* in the program" from
    §9.4.2/4. [my emphasis]

    Regards

    Paul Bibbings
     
    Paul Bibbings, Jun 20, 2010
    #10
  11. * Paul Bibbings, on 20.06.2010 16:44:
    > "Alf P. Steinbach /Usenet"<> writes:
    >
    >> Consider the following program (off the cuff):
    >>
    >> struct S
    >> {
    >> static int const v = 42;
    >> };
    >>
    >> #include<iostream>
    >> int main()
    >> { std::cout<< S::v<< std::endl; }
    >>
    >> Here the definition of S::v is not required to exist.
    >>
    >> And in fact there is no definition of S::v, just a pure declaration. :)
    >>
    >> It may look as if there is a definition of S::v, but it really is just
    >> a pure declaration.
    >>
    >> A definition would be, at namespace scope,
    >>
    >> int const S::v;
    >>
    >> which might look like a pure declaration, but is really a definition.

    >
    > The reference for this is [class.static.data] §9.4.2/4
    >
    > "If a static data member is of const integral or const enumeration
    > type, its declaration in the class definition can specify a
    > constant-initializer which shall be an integral constant expression
    > (5.19). In that case, the member can appear in integral constant
    > expressions. The member shall still be defined in a namespace scope
    > if it is used in the program and the namespace scope definition shall
    > not contain an initializer."
    >
    > But, from my reading of this subclause, I don't feel certain that the
    > above example - which gcc is happy with - is correct.
    >
    > The reason for my doubt stems from [basic.def.odr] §3.2/2:
    >
    > "An expression is /potentially evaluated/ unless it appears where an
    > integral constant expression is required... An object or
    > non-overloaded function is /used/ if its name appears in a
    > potentially-evaluated expression. ..."
    >
    > What this means, in effect - if I am reading it correctly - is that
    > "appear[ance] where an integral constant expression is required" doesn't
    > constitute a "use." Therefore the last sentence of §9.4.2/4 doesn't
    > apply in this case, and the following is valid, IIUC:
    >
    > struct S
    > {
    > static const int v = 42;
    > static const int w = v - 1; // #1
    > }
    >
    > In this case, in line #1, v "appears where an integral constant
    > expression is required" and thus a definition (according to §9.4.2/4) is
    > *not* required.
    >
    > However, since (from your code), the call:
    >
    > std::cout<< S::v<< std::endl;
    >
    > invokes:
    >
    > std::basic_ostream<char, std::char_traits<char> >&
    > std::eek:stream::eek:perator<<(int);
    >
    > I can't see that we have the same situation here. Matching the int
    > parameter to the call to std::eek:stream::eek:p<< does *not*, in my view,
    > constitute a context "where an integral constant expression is
    > required." This seems clear from the fact that:
    >
    > int i = 42;
    > std::cout<< i<< std::endl; // #2
    >
    > works, where:
    >
    > template<int N> // requires const. integral expr. as argument
    > struct A { };
    > A<i> a;
    >
    > does not. Therefore, the expression `i' (in #2) seems to constitute a
    > `use' according to §3.2/2 and thereby invokes "The member shall still
    > be defined in a namespace scope if it is *used* in the program" from
    > §9.4.2/4. [my emphasis]


    Regarding the formal you're right, AFAICS.

    However, that's only in the formal, and only because the committee goofed. :)

    The original issue was raised by Bill Gibbons in

    http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_closed.html#82

    where in particular he mentions

    "Use as an integral constant expression. Only the value is used. A static
    data member with its initializer given in the class need not have a
    namespace-scope definition."

    The committee IMHO incorrectly decided that it was a duplicate of an earlier
    issue raised by Bill, effectively that the main point of #83 was not a
    "subtantive part" of the issue, and tackled instead Bill's earlier

    http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#48

    by resolving it to the text you quoted.

    I think it might be a good idea to post this to [comp.std.c++].

    It's really bad when /formally/ a compile time constant can't be used as such; I
    can't imagine that that was the intent (as evident from the quote above it was
    certainly not Bill's intent). In the above program S::v is used as integral
    constant expression. The problem wrt. to the formal is that for this context an
    integral constant expression is not "required", it's just permitted...


    Cheers,

    - Alf

    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach /Usenet, Jun 20, 2010
    #11
  12. "Alf P. Steinbach /Usenet" <> writes:

    > * Paul Bibbings, on 20.06.2010 16:44:
    >> "Alf P. Steinbach /Usenet"<> writes:
    >>
    >>> Consider the following program (off the cuff):
    >>>
    >>> struct S
    >>> {
    >>> static int const v = 42;
    >>> };
    >>>
    >>> #include<iostream>
    >>> int main()
    >>> { std::cout<< S::v<< std::endl; }
    >>>
    >>> Here the definition of S::v is not required to exist.
    >>>
    >>> And in fact there is no definition of S::v, just a pure declaration. :)
    >>>
    >>> It may look as if there is a definition of S::v, but it really is just
    >>> a pure declaration.
    >>>
    >>> A definition would be, at namespace scope,
    >>>
    >>> int const S::v;
    >>>
    >>> which might look like a pure declaration, but is really a definition.

    >>
    >> The reference for this is [class.static.data] §9.4.2/4
    >>
    >> "If a static data member is of const integral or const enumeration
    >> type, its declaration in the class definition can specify a
    >> constant-initializer which shall be an integral constant expression
    >> (5.19). In that case, the member can appear in integral constant
    >> expressions. The member shall still be defined in a namespace scope
    >> if it is used in the program and the namespace scope definition shall
    >> not contain an initializer."
    >>
    >> But, from my reading of this subclause, I don't feel certain that the
    >> above example - which gcc is happy with - is correct.
    >>
    >> The reason for my doubt stems from [basic.def.odr] §3.2/2:
    >>
    >> "An expression is /potentially evaluated/ unless it appears where an
    >> integral constant expression is required... An object or
    >> non-overloaded function is /used/ if its name appears in a
    >> potentially-evaluated expression. ..."
    >>
    >> What this means, in effect - if I am reading it correctly - is that
    >> "appear[ance] where an integral constant expression is required" doesn't
    >> constitute a "use." Therefore the last sentence of §9.4.2/4 doesn't
    >> apply in this case, and the following is valid, IIUC:
    >>
    >> struct S
    >> {
    >> static const int v = 42;
    >> static const int w = v - 1; // #1
    >> }
    >>
    >> In this case, in line #1, v "appears where an integral constant
    >> expression is required" and thus a definition (according to §9.4.2/4) is
    >> *not* required.
    >>
    >> However, since (from your code), the call:
    >>
    >> std::cout<< S::v<< std::endl;
    >>
    >> invokes:
    >>
    >> std::basic_ostream<char, std::char_traits<char> >&
    >> std::eek:stream::eek:perator<<(int);
    >>
    >> I can't see that we have the same situation here. Matching the int
    >> parameter to the call to std::eek:stream::eek:p<< does *not*, in my view,
    >> constitute a context "where an integral constant expression is
    >> required." This seems clear from the fact that:
    >>
    >> int i = 42;
    >> std::cout<< i<< std::endl; // #2
    >>
    >> works, where:
    >>
    >> template<int N> // requires const. integral expr. as argument
    >> struct A { };
    >> A<i> a;
    >>
    >> does not. Therefore, the expression `i' (in #2) seems to constitute a
    >> `use' according to §3.2/2 and thereby invokes "The member shall still
    >> be defined in a namespace scope if it is *used* in the program" from
    >> §9.4.2/4. [my emphasis]

    >
    > Regarding the formal you're right, AFAICS.
    >
    > However, that's only in the formal, and only because the committee goofed. :)
    >
    > The original issue was raised by Bill Gibbons in
    >
    > http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_closed.html#82
    >
    > where in particular he mentions
    >
    > "Use as an integral constant expression. Only the value is used. A static
    > data member with its initializer given in the class need not have a
    > namespace-scope definition."
    >
    > The committee IMHO incorrectly decided that it was a duplicate of an
    > earlier issue raised by Bill, effectively that the main point of #83
    > was not a "subtantive part" of the issue, and tackled instead Bill's
    > earlier
    >
    > http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_defects.html#48
    >
    > by resolving it to the text you quoted.
    >
    > I think it might be a good idea to post this to [comp.std.c++].
    >
    > It's really bad when /formally/ a compile time constant can't be used
    > as such; I can't imagine that that was the intent (as evident from the
    > quote above it was certainly not Bill's intent). In the above program
    > S::v is used as integral constant expression. The problem wrt. to the
    > formal is that for this context an integral constant expression is not
    > "required", it's just permitted...


    That's interesting and, following through your references, I would agree
    with your assessment of what happened; that, in resolving the earlier
    issue, a key part of Gibbon's later DR was missed.

    I see that the wording has changed in the FCD with the current (C++03):

    C++03: [basic.def.odr] §3.2/2
    "An object or non-overloaded function is /used/ if its name appears
    in a potentially-evaluated expression."

    where (ibid.):

    "An expression is /potentially evaluated/ unless it appears where an
    integral constant expression is required (see 5.19),..."

    becoming:
    C++0x FCD: [basic.def.odr] $3.2/2
    "A variable or non-overloaded function whose name appears as a
    potentially-evaluated expression is /used/ unless it is an object
    that satisfies the requirements for appearing in a constant
    expression (5.19) and the lvalue-to-rvalue conversion (4.1) is
    immediately applied."

    where (ibid.):

    "An expression is /potentially evaluated/ unless it is an unevaluated
    operand (Clause 5) or a subexpression thereof."

    I think that the changes are significant and, in the face of the
    introduction of constexpr, §5.19 [expr.const] is greatly changed also.
    I haven't followed through all the details in relation to the formal
    correctness (or not) of the above code under C++0x but what jumps out at
    me in particular are:

    1. The removal of "unless it appears where an integral constant
    expression is required" from the definition of
    `potentially-evaluated'; followed by
    2. Its reappearance in the definition of `used' where it is reworded
    as "unless it is an object that "satisfies the requirements for
    appearing in a constant expression."

    I am wondering, then, if the specific change from "appears where an
    integral constant expression is required" to "satisfies the requirements
    for appearing in a constant expression" makes all the difference for the
    code under consideration. Clearly, S::v *does* satisfy the requirements
    for appearing in a constant expression, etc. and so, to my reading, its
    appearance in:

    std::cout << S::v << std::endl;

    doesn't constitute a use in the new sense and so the last sentence of
    (the unchanged) §9.4.2/4 doesn't apply - *no* definition required.

    Regards

    Paul Bibbings
     
    Paul Bibbings, Jun 20, 2010
    #12
  13. Paul Bibbings

    James Kanze Guest

    On Jun 19, 11:15 pm, Paul Bibbings <> wrote:
    > I've been working on some code (not mine) that relies on the
    > following motif leading to a compilation failure, with
    > sensible output, at line 17 (below). The trouble is, with
    > gcc-4.4.3, it *doesn't* fail:


    > template<typename T>
    > struct Error { };


    > template<typename T>
    > struct A
    > {
    > static const int i = Error<T>::TempArgMustBeInt; // line: 7
    > };


    > template<>
    > struct A<int>
    > { };


    > int main()
    > {
    > A<int> A_int;
    > A<char> A_char; // line: 17
    > }


    > I'm thinking that it probably should (or, I have no immediate
    > understanding of why it wouldn't); whoever wrote the original code
    > (probably some while ago) thought it should; and, Comeau seems to
    > agree:


    > Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for
    > ONLINE_EVALUATION_BETA2
    > Copyright 1988-2008 Comeau Computing. All rights reserved.
    > MODE:strict errors C++ noC++0x_extensions


    > "ComeauTest.c", line 7: error: class "Error<char>" has no member
    > "TempArgMustBeInt"
    > static const int i = Error<T>::TempArgMustBeInt;
    > ^
    > detected during instantiation of class "A<T> [with T=char]" at
    > line 17
    > // ...


    > 1 error detected in the compilation of "ComeauTest.c".


    > What does anyone else think?


    I think the standard is a bit vague about this. According to
    the standard, a static data member is only instantiated if it is
    used. (See §14.7.1.) I don't see anything in your code which
    would require an instance of A::i, so the definition of A::i
    should not be instantiated. Which is good, because you don't
    provide a definition (in the sense of the standard). On the
    other hand, this is the only case I know in the standard where
    a declaration which is not a definition can contain an
    initializer---is the initializer considered part of the
    declaration, or a pre-definition, of sorts? In the first case,
    it should be instantiated (triggering the error); in the second,
    it shouldn't be instantiated unless i is actually used.

    I think the classical way of doing this is something like:

    template <typename T>
    struct A
    {
    enum { error = sizeof(Error<T>::whatever) };
    };

    The enum constant must be instantiated any time the class is.

    --
    James Kanze
     
    James Kanze, Jun 22, 2010
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Andrew Thompson
    Replies:
    9
    Views:
    467
    Andrew Thompson
    May 6, 2004
  2. JKop
    Replies:
    16
    Views:
    1,140
  3. Guido van Rossum

    Rejecting the J2 decorators proposal

    Guido van Rossum, Sep 1, 2004, in forum: Python
    Replies:
    2
    Views:
    289
    Nicolas Fleury
    Sep 1, 2004
  4. JohnQ
    Replies:
    0
    Views:
    322
    JohnQ
    Aug 26, 2007
  5. r0g
    Replies:
    1
    Views:
    324
Loading...

Share This Page