friend 'operator new' in inheritance as a template

Discussion in 'C++' started by wo3kie@gmail.com, May 29, 2008.

  1. Guest

    #include <iostream>
    #include <map>
    #include <utility>

    //
    // Base
    // / | \
    // Derived1 Derived2 \
    // \ | /
    // Derived3
    //

    template< typename _T >
    struct Base {
    friend void * operator new ( size_t _size ){
    std::cout << typeid( _T ).name() << std::endl;
    return malloc( _size );
    }
    };

    struct Derived1 : Base< Derived1 > {
    };

    struct Derived2 : Base< Derived2 >{
    };

    struct Derived3 : Derived1, Derived2, Base< Derived3 >{
    };

    int main(){
    Derived1 * d1 = new Derived1; // prints 8Derived3
    Derived2 * d2 = new Derived2; // prints 8Derived3
    }
    , May 29, 2008
    #1
    1. Advertising

  2. Guest

    I am sorry, with copy/paste I clear message

    May anyony explain me how `operator new' templates argument is deduced
    as Derived3?
    , May 29, 2008
    #2
    1. Advertising

  3. Daniel Pitts Guest

    Paavo Helde wrote:
    > "" <> kirjutas:
    >> template< typename _T >

    >
    > Undefined behavior, you are not allowed to use such identifiers in your
    > code (beginning underscore followed by a capital letter). However, this
    > is probably not the reason of your concern.

    I thought identifiers could start with underscore (or even be
    underscore), why is it undefined?

    --
    Daniel Pitts' Tech Blog: <http://virtualinfinity.net/wordpress/>
    Daniel Pitts, Jun 14, 2008
    #3
  4. On 2008-06-14 22:28, Daniel Pitts wrote:
    > Paavo Helde wrote:
    >> "" <> kirjutas:
    >>> template< typename _T >

    >>
    >> Undefined behavior, you are not allowed to use such identifiers in your
    >> code (beginning underscore followed by a capital letter). However, this
    >> is probably not the reason of your concern.

    > I thought identifiers could start with underscore (or even be
    > underscore), why is it undefined?


    Identifiers starting with an underscore followed by a capital letter are
    reserved for the implementation, also identifiers in the global
    namespace beginning with an underscore are also reserved (so you can
    only use them inside functions, classes, and namespaces.

    --
    Erik Wikström
    Erik Wikström, Jun 14, 2008
    #4
  5. James Kanze Guest

    On Jun 14, 9:36 pm, Paavo Helde <> wrote:
    > "" <> kirjutas:


    > > #include <iostream>
    > > #include <map>
    > > #include <utility>


    > > //
    > > // Base
    > > // / | \
    > > // Derived1 Derived2 \
    > > // \ | /
    > > // Derived3
    > > //


    > > template< typename _T >
    > > struct Base {
    > > friend void * operator new ( size_t _size ){
    > > std::cout << typeid( _T ).name() << std::endl;


    > As far as I understand, operator new cannot be a template, and
    > a friend declaration does not make it into a template
    > automatically anyway, so the _T symbol should not be visible
    > inside the function. If the compiler still compiles this, I
    > think this is a compiler bug.


    The whole thing is fishy. This basically says that 1) the
    global operator new is a friend of Base, and 2) provides an
    implementation of the global operator new. Which is, I guess,
    legal. Once... because this is a template, it's going to
    provide a new implementation of the global operator new each
    time the template is instantiated. Which is a violation of the
    one definition rule, and so undefined behavior. (Since the
    function is defined *in* the template, I'm pretty sure that the
    use of _T is legal.)

    There's also the problem that because the function is defined in
    a class definition, it is inline; the global operator new
    function is not allowed to be inline (for the obvious reason
    that it's likely to have been used in some library code
    somewhere, and that code won't see your inline definition).

    > > return malloc( _size );


    Which creates a couple of additional problems: if malloc fails,
    his operator new is going to return a null pointer, rather than
    raising a bad_alloc exception. And he hasn't provided a
    corresponding operator delete (and of course, he has no idea as
    to how the standard operator delete will try to free the
    memory).

    > > }
    > > };


    > > struct Derived1 : Base< Derived1 > {
    > > };


    > > struct Derived2 : Base< Derived2 >{
    > > };


    > > struct Derived3 : Derived1, Derived2, Base< Derived3 >{
    > > };


    Note that the code doesn't correspond to the diagram in comments
    at the top.

    > > int main(){
    > > Derived1 * d1 = new Derived1; // prints 8Derived3
    > > Derived2 * d2 = new Derived2; // prints 8Derived3
    > > }


    > You have provided illegal source to the compiler; the results
    > can be whatever. I am not sure if the compiler is obliged to
    > produce a diagnostic; I suspect it is. Maybe you should file a
    > bug report to the compiler provider.


    It's undefined behavior, but I can guess what is happening.
    He's instantiated the template three times, which provides three
    different implementations of the global operator new. The
    compiler just happened to use the last one it saw.

    --
    James Kanze (GABI Software) email:
    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
    James Kanze, Jun 15, 2008
    #5
  6. James Kanze Guest

    On Jun 15, 9:24 pm, Paavo Helde <> wrote:
    > James Kanze <> kirjutas:
    > > On Jun 14, 9:36 pm, Paavo Helde <> wrote:
    > >> "" <> kirjutas:


    > >> > #include <iostream>
    > >> > #include <map>
    > >> > #include <utility>


    > >> > //
    > >> > // Base
    > >> > // / | \
    > >> > // Derived1 Derived2 \
    > >> > // \ | /
    > >> > // Derived3
    > >> > //


    > >> > template< typename _T >
    > >> > struct Base {
    > >> > friend void * operator new ( size_t _size ){
    > >> > std::cout << typeid( _T ).name() << std::endl;


    > >> As far as I understand, operator new cannot be a template, and
    > >> a friend declaration does not make it into a template
    > >> automatically anyway, so the _T symbol should not be visible
    > >> inside the function. If the compiler still compiles this, I
    > >> think this is a compiler bug.


    > > The whole thing is fishy. This basically says that 1) the
    > > global operator new is a friend of Base, and 2) provides an
    > > implementation of the global operator new. Which is, I guess,
    > > legal. Once... because this is a template, it's going to
    > > provide a new implementation of the global operator new each
    > > time the template is instantiated. Which is a violation of the
    > > one definition rule, and so undefined behavior. (Since the
    > > function is defined *in* the template, I'm pretty sure that the
    > > use of _T is legal.)


    > Yes, I somehow assumed that the definition of the friend function should
    > not depend on whether it is "inline" in the friend declaration or not.
    > And if it is not, then _T can't be visible in the function body. And _T
    > is not participating in the function signature, so it would be impossible
    > to forward this to the non-inline non-template function definition.
    > right?


    > But anyway, as Comeau online does not complain about use of _T (renamed
    > to T to be sure), it seems my assumption was wrong. So the meaning of the
    > function body can differ depending on if it is defined inline in the
    > friend declaration, or separately. You learn something new every day...


    What's legal in the inline definition of a friend doesn't depend
    on the signature of the friend. And things like:

    template< typename Derived >
    class ArithmeticOperators
    {
    friend Derived
    operator+( Derived const& lhs, Derived const& rhs )
    {
    Derived result( lhs ) ;
    result += rhs ;
    return result ;
    }
    // ...
    } ;

    Are more or less standard practice, and have been since Barton
    and Nackman invented the idiom (or even before that, if they
    learned it from somewhere else). (In this case, of course,
    there's no problem with the one definition rule, because each
    instantiation of ArithmeticOperators will define a different
    operator+.)

    > [...]


    > >> > struct Derived1 : Base< Derived1 > {
    > >> > };


    > >> > struct Derived2 : Base< Derived2 >{
    > >> > };


    > >> > struct Derived3 : Derived1, Derived2, Base< Derived3 >{
    > >> > };


    > > Note that the code doesn't correspond to the diagram in comments
    > > at the top.


    > How is that? I can't see anything wrong here.


    His diagram is:

    Base
    / | \
    Derived1 Derived2 \
    \ | /
    Derived3

    His code implements:

    Base Base
    | |
    Derived1 Derived2 Base
    \ | /
    Derived3

    Not at all the same thing.

    > >> > int main(){
    > >> > Derived1 * d1 = new Derived1; // prints 8Derived3
    > >> > Derived2 * d2 = new Derived2; // prints 8Derived3
    > >> > }


    > >> You have provided illegal source to the compiler; the results
    > >> can be whatever. I am not sure if the compiler is obliged to
    > >> produce a diagnostic; I suspect it is. Maybe you should file a
    > >> bug report to the compiler provider.


    > > It's undefined behavior, but I can guess what is happening.
    > > He's instantiated the template three times, which provides three
    > > different implementations of the global operator new. The
    > > compiler just happened to use the last one it saw.


    > Yes, you are right, this appears to be what gcc is doing. It
    > seems VC++ 9.0 detects the multiple definitions error:


    > error C2084: function 'void *Base<T>::eek:perator new(size_t)' already has a
    > body


    > It seems Comeau online has the same opinion:


    > "ComeauTest.c", line 15: error: function "operator new(size_t)" has
    > already been
    > defined
    > friend void * operator new ( size_t _size ){
    > ^
    > detected during instantiation of class "Base<T> [with
    > T=Derived2]"


    > (In addition to mention an error regarding the incompatible throw
    > specification).


    Well, formally, it's undefined behavior (I think), so g++ isn't
    wrong. But frankly, in this case, I'd expect an error message
    as well.

    --
    James Kanze (GABI Software) email:
    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
    James Kanze, Jun 15, 2008
    #6
    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. Yu Lianqing
    Replies:
    1
    Views:
    626
    Leor Zolman
    Apr 4, 2004
  2. cppsks
    Replies:
    0
    Views:
    803
    cppsks
    Oct 27, 2004
  3. CoolPint
    Replies:
    3
    Views:
    479
    Victor Bazarov
    Feb 13, 2005
  4. Amadeus W. M.

    friend operator<< in template classes

    Amadeus W. M., Jan 22, 2006, in forum: C++
    Replies:
    4
    Views:
    441
    Amadeus W. M.
    Jan 22, 2006
  5. A L
    Replies:
    1
    Views:
    503
    Alf P. Steinbach /Usenet
    Aug 25, 2010
Loading...

Share This Page