Calling constructors and temporary objects

Discussion in 'C++' started by David Given, Jun 13, 2008.

  1. David Given

    David Given Guest

    (No, I'm *not* trying to call a constructor from another constructor!)

    I have this class:

    class P : public std::stringstream
    {
    public:
    ~P() { std::cout << str(); }
    };

    That is, it behaves like a stringstream, but on destruct it prints
    itself. This allows me to do this:

    {
    P p;
    p << "Hello, world!";
    }

    ....and I get 'Hello world!' on stdout.

    However, if instead I do this:

    P() << "Hello world!";

    ....I get '0x12345678' instead (that is, some random hex number).

    Obviously what's happening is that it's not finding the const char*
    iostream method on P, and is falling back to the void* one; but I don't
    understand why. The temporary P instance is getting created and
    destructed at the appropriate points, but it's behaving as if it's lost
    some of its stringstream methods.

    I use this idiom extensively for various printers and loggers and so on;
    they all work fine. This is the first time I've tried it with a subclass
    of stringstream, though; all the others have delegated to an embedded
    instance of stringstream rather than subclassing it. I thought I'd save
    myself some typing this way. But why doesn't it work? More importantly,
    why is it not working in this really bizarre fashion?

    --
    ┌─── dg@cï½ï½—lï½ï½’k.cï½ï½ ───── http://www.cowlark.com ─────
    │ "I have always wished for my computer to be as easy to use as my
    │ telephone; my wish has come true because I can no longer figure out
    │ how to use my telephone." --- Bjarne Stroustrup
     
    David Given, Jun 13, 2008
    #1
    1. Advertising

  2. David Given

    Kai-Uwe Bux Guest

    David Given wrote:

    > (No, I'm *not* trying to call a constructor from another constructor!)
    >
    > I have this class:
    >
    > class P : public std::stringstream
    > {
    > public:
    > ~P() { std::cout << str(); }
    > };
    >
    > That is, it behaves like a stringstream, but on destruct it prints
    > itself. This allows me to do this:
    >
    > {
    > P p;
    > p << "Hello, world!";
    > }
    >
    > ...and I get 'Hello world!' on stdout.
    >
    > However, if instead I do this:
    >
    > P() << "Hello world!";
    >
    > ...I get '0x12345678' instead (that is, some random hex number).
    >
    > Obviously what's happening is that it's not finding the const char*
    > iostream method on P, and is falling back to the void* one; but I don't
    > understand why.


    The reason is that one is a member function (the one that is found) and the
    other is a free function that is rejected as a match because the compiler
    is not allowed to bind the non-const stream reference to the temporary P().


    > The temporary P instance is getting created and
    > destructed at the appropriate points, but it's behaving as if it's lost
    > some of its stringstream methods.
    >
    > I use this idiom extensively for various printers and loggers and so on;
    > they all work fine. This is the first time I've tried it with a subclass
    > of stringstream, though; all the others have delegated to an embedded
    > instance of stringstream rather than subclassing it. I thought I'd save
    > myself some typing this way. But why doesn't it work? More importantly,
    > why is it not working in this really bizarre fashion?


    The culprit is clause [8.5.3/5].

    Note: your case is similar to this

    std::vector<int>().swap( my_vector ); // legal
    swap( std::vector<int>(), my_vector ); // not legal
    my_vector.swap( std::vector<int>() ); // not legal

    The difference is that the compiler finds a match using a conversion to
    void* after all illegal readings have been ruled out. It doesn't really
    make all that much sense, but this is the state of affairs in C++.


    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Jun 13, 2008
    #2
    1. Advertising

  3. David Given

    David Given Guest

    Kai-Uwe Bux wrote:
    [...]
    > The reason is that one is a member function (the one that is found) and the
    > other is a free function that is rejected as a match because the compiler
    > is not allowed to bind the non-const stream reference to the temporary P().

    [...]
    > The culprit is clause [8.5.3/5].


    If I've understood this correctly, this is all due to not being able to
    initialise a non-const reference from a temporary, right?

    Here's a cleaner test case that fails in the same way (on gcc. MSVC is
    quite happy with it, annoyingly enough.)

    struct SUPER { };
    struct SUB : SUPER { };
    SUPER& operator<<(SUPER& s, int i) {}

    { SUB() << 1; }

    Given that the temporary goes out of scope at the end of the statement,
    I don't see why this restriction is in place --- it should be possible
    for the compiler to implicitly bind the reference, pass it to
    operator<<(), have operator<<() work on it and then return it, and then
    have the temporary get destructed. It's precisely the same semantic as
    if I'd used a non-temporary.

    So, why isn't this possible, and are there any workarounds? Could I have
    SUB automatically cast itself to an object of the right type, for example?

    --
    ┌─── dg@cï½ï½—lï½ï½’k.cï½ï½ ───── http://www.cowlark.com ─────
    │ "I have always wished for my computer to be as easy to use as my
    │ telephone; my wish has come true because I can no longer figure out
    │ how to use my telephone." --- Bjarne Stroustrup
     
    David Given, Jun 14, 2008
    #3
  4. David Given

    Kai-Uwe Bux Guest

    David Given wrote:

    > Kai-Uwe Bux wrote:
    > [...]
    >> The reason is that one is a member function (the one that is found) and
    >> the other is a free function that is rejected as a match because the
    >> compiler is not allowed to bind the non-const stream reference to the
    >> temporary P().

    > [...]
    >> The culprit is clause [8.5.3/5].

    >
    > If I've understood this correctly, this is all due to not being able to
    > initialise a non-const reference from a temporary, right?


    Yes.

    > Here's a cleaner test case that fails in the same way (on gcc. MSVC is
    > quite happy with it, annoyingly enough.)
    >
    > struct SUPER { };
    > struct SUB : SUPER { };
    > SUPER& operator<<(SUPER& s, int i) {}
    >
    > { SUB() << 1; }
    >
    > Given that the temporary goes out of scope at the end of the statement,
    > I don't see why this restriction is in place --- it should be possible
    > for the compiler to implicitly bind the reference, pass it to
    > operator<<(), have operator<<() work on it and then return it, and then
    > have the temporary get destructed. It's precisely the same semantic as
    > if I'd used a non-temporary.
    >
    > So, why isn't this possible, and are there any workarounds? Could I have
    > SUB automatically cast itself to an object of the right type, for example?


    You can make sure that there is always a matching member function:

    struct P : public std::stringstream {

    template < typename T >
    P & operator<< ( T const & t ) {
    static_cast< std::stringstream & >( *this ) << t;
    return ( *this );
    }

    ~P() { std::cout << str(); }
    };


    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Jun 14, 2008
    #4
  5. David Given

    David Given Guest

    Kai-Uwe Bux wrote:
    [...]
    > You can make sure that there is always a matching member function:
    >
    > struct P : public std::stringstream {
    >
    > template < typename T >
    > P & operator<< ( T const & t ) {
    > static_cast< std::stringstream & >( *this ) << t;
    > return ( *this );
    > }


    Hmm. I hadn't thought of using templates in this way. That's very
    interesting --- I may be able to apply it to my old delegation approach,
    too.

    Okay, I'll go and do more investigation. Thanks for the assistance!

    --
    ┌─── dg@cï½ï½—lï½ï½’k.cï½ï½ ───── http://www.cowlark.com ─────
    │ "I have always wished for my computer to be as easy to use as my
    │ telephone; my wish has come true because I can no longer figure out
    │ how to use my telephone." --- Bjarne Stroustrup
     
    David Given, Jun 14, 2008
    #5
    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. Dave Rudolf
    Replies:
    12
    Views:
    8,324
    Martijn Lievaart
    Feb 6, 2004
  2. Jeremy Smith
    Replies:
    2
    Views:
    600
    Jeremy Smith
    Aug 3, 2006
  3. Jess
    Replies:
    5
    Views:
    615
    Ron Natalie
    Jun 7, 2007
  4. Peng Yu
    Replies:
    5
    Views:
    403
    Juha Nieminen
    Sep 19, 2008
  5. Replies:
    4
    Views:
    467
Loading...

Share This Page