tricky stringstream-based temporary

Discussion in 'C++' started by Kai-Uwe Bux, Jun 11, 2004.

  1. Kai-Uwe Bux

    Kai-Uwe Bux Guest

    Hi folks,


    I have trouble writing a class, derving from stringstream, that collects
    item and once it's done will write them to std::cout in one go. It works
    fine except when I use it as a temporary. Here is a tiny test programm:

    #include <iostream>
    #include <sstream>

    class Gatherer : public std::stringstream {
    public:

    ~Gatherer ( void ) {
    std::cout << this->str() << std::endl;
    }

    };

    int main ( void ) {
    {
    Gatherer() << "hello world!";
    }
    {
    Gatherer gather;
    gather << "hello world!";
    }
    {
    Gatherer() << std::dec << "hello world!";
    }
    }


    On my machine, it prints:

    0x8049e48
    hello world!
    hello world!

    So, clearly the first line is an address. I have no explanation for this. I
    am especially puzzled by the third line. Inserting a std::dec should not
    have this effect, or should it?


    Best regards

    Kai-Uwe
     
    Kai-Uwe Bux, Jun 11, 2004
    #1
    1. Advertising

  2. "Kai-Uwe Bux" <> wrote...
    > I have trouble writing a class, derving from stringstream, that collects
    > item and once it's done will write them to std::cout in one go. It works
    > fine except when I use it as a temporary. Here is a tiny test programm:
    >
    > #include <iostream>
    > #include <sstream>
    >
    > class Gatherer : public std::stringstream {
    > public:
    >
    > ~Gatherer ( void ) {
    > std::cout << this->str() << std::endl;
    > }
    >
    > };
    >
    > int main ( void ) {
    > {
    > Gatherer() << "hello world!";
    > }
    > {
    > Gatherer gather;
    > gather << "hello world!";
    > }
    > {
    > Gatherer() << std::dec << "hello world!";
    > }
    > }
    >
    >
    > On my machine, it prints:
    >
    > 0x8049e48
    > hello world!
    > hello world!
    >
    > So, clearly the first line is an address. I have no explanation for this.

    I
    > am especially puzzled by the third line. Inserting a std::dec should not
    > have this effect, or should it?


    I think you're running into synchronisation problem. When compiled
    with Intel v4.5 or VC++ v6sp5, this code outputs three hello. When
    compiled with VC++ v7.1, it prints an address first and then two
    hello, just like you described.

    I think what you get is that by the time the destructor is executing,
    all bets are off WRT the contents of this->str(), it may not contain
    anything, or return a valid object... You might be incurring the UB.
    Just a guess.

    Victor
     
    Victor Bazarov, Jun 11, 2004
    #2
    1. Advertising

  3. Kai-Uwe Bux

    Kai-Uwe Bux Guest

    Victor Bazarov wrote:

    > "Kai-Uwe Bux" <> wrote...
    >> I have trouble writing a class, derving from stringstream, that collects
    >> item and once it's done will write them to std::cout in one go. It works
    >> fine except when I use it as a temporary. Here is a tiny test programm:
    >>
    >> #include <iostream>
    >> #include <sstream>
    >>
    >> class Gatherer : public std::stringstream {
    >> public:
    >>
    >> ~Gatherer ( void ) {
    >> std::cout << this->str() << std::endl;
    >> }
    >>
    >> };
    >>
    >> int main ( void ) {
    >> {
    >> Gatherer() << "hello world!";
    >> }
    >> {
    >> Gatherer gather;
    >> gather << "hello world!";
    >> }
    >> {
    >> Gatherer() << std::dec << "hello world!";
    >> }
    >> }
    >>
    >>
    >> On my machine, it prints:
    >>
    >> 0x8049e48
    >> hello world!
    >> hello world!
    >>
    >> So, clearly the first line is an address. I have no explanation for this.

    > I
    >> am especially puzzled by the third line. Inserting a std::dec should not
    >> have this effect, or should it?

    >
    > I think you're running into synchronisation problem. When compiled
    > with Intel v4.5 or VC++ v6sp5, this code outputs three hello. When
    > compiled with VC++ v7.1, it prints an address first and then two
    > hello, just like you described.
    >
    > I think what you get is that by the time the destructor is executing,
    > all bets are off WRT the contents of this->str(), it may not contain
    > anything, or return a valid object... You might be incurring the UB.
    > Just a guess.
    >
    > Victor


    Thanks for your help.

    I eliminated inheritance as a possible source of the problem:

    #include <fstream>

    int main ( void ) {
    std::eek:fstream( "greeting.out" ) << "hello world!\n";
    }


    This compiles fine. But it does unexpected things (well, as of now, I
    actually was expecting this):

    news_group> cat greeting.out
    0x8048a01news_group>


    So the problem definitely lies in the fact that a temporary is created.
    Does the standard say that temporary streams have undefined behaviour? I
    was under the impression that a temporary is supposed to behave exactly
    like the real thing except that for its life time there are only very few
    guarantees (like it will live at least to see the completion of the
    expression of which it is a part).

    As of now, I am considering the possibility that it could be a bug in my
    standard library.


    Thanks again

    Kai-Uwe
     
    Kai-Uwe Bux, Jun 11, 2004
    #3
  4. Kai-Uwe Bux wrote:
    >
    > int main ( void ) {
    > {
    > Gatherer() << "hello world!";
    > }
    > {
    > Gatherer gather;
    > gather << "hello world!";
    > }
    > {
    > Gatherer() << std::dec << "hello world!";
    > }
    > }


    You're not supposed to pass a temporary for a non-const reference
    parameter (in this case, the first parameter of operator<<). The result
    of doing so is undefined.

    --
    Russell Hanneken

    Use ROT13 to decode my email address.
     
    Russell Hanneken, Jun 11, 2004
    #4
  5. Kai-Uwe Bux

    Kai-Uwe Bux Guest

    Russell Hanneken wrote:

    > Kai-Uwe Bux wrote:
    >>
    >> int main ( void ) {
    >> {
    >> Gatherer() << "hello world!";
    >> }
    >> {
    >> Gatherer gather;
    >> gather << "hello world!";
    >> }
    >> {
    >> Gatherer() << std::dec << "hello world!";
    >> }
    >> }

    >
    > You're not supposed to pass a temporary for a non-const reference
    > parameter (in this case, the first parameter of operator<<). The result
    > of doing so is undefined.
    >


    First off all: thank you. I was really puzzled.

    Second: Could you direct me to the point in the standard where I find this.
    I was just reading 12.2.5:

    .... A temporary bound to a reference parameter in a function call (5.2.2)
    persists until the completion of the full expression containing the call.
    ....

    Wouldn't that have been a great place to put in a warnig? Please do not
    get me wrong, I do not doubt that you are right -- I am just curious.


    Thanks again

    Kai-Uwe
     
    Kai-Uwe Bux, Jun 11, 2004
    #5
  6. Kai-Uwe Bux

    Siemel Naran Guest

    Kai-Uwe Bux wrote:

    > {
    > Gatherer() << "hello world!";
    > }


    Some background:

    (1) When you call a function f(T()) then the function signature must be
    either f(T) or f(const T&) but not f(T&). In other words, if the signature
    is f(T&) then the argument cannot be a temporary.

    (2) When you call a function T().f() then the function signature of T::f may
    be either T::f() const or T::f(). In other words, you can call a non-const
    member function on a temporary. This is a contrast to (1).

    The reason my you see a number is that because of

    Gatherer() <<

    the compiler spots a temporary, and looks for member overloads
    Gatherer::eek:perator<< that match the function arguments. Now operator<<
    const void* is implemented as a member, but operator<< const char * is
    implemented as a non-member.

    So while we wish the code calls

    std::eek:stream<char>& std::eek:perator<<(ostream&, const char *);

    it really calls only std::eek:stream::eek:perator<<(const void *);

    As a QoI, a good compiler would probably look for both members and
    non-members to call, and if it finds the non-member function is the right
    one to call, issue an error or warning that the first argument cannot be a
    temporary -- or at least issue an ambiguity between the two possible
    operator<< to call.

    So what you're seeing is the address of "hello world!". Try const char * s
    = "hello world!" and then Gatherer() << s, followed by cout << (const void
    *)(s), and see what happens



    > {
    > Gatherer gather;
    > gather << "hello world!";
    > }


    Works as expected.

    > {
    > Gatherer() << std::dec << "hello world!";
    > }
    > }


    Also works as expected. Fortunately Gatherer() << std::dec calls a member
    function

    basic_ostream::eek:perator<<(ios_base& (*)(ios_base&))

    and for member functions you are allowed to call non-const member functions
    on a temporary. So this sets the decimal mode, returns an ostream&, adds
    text to the ostream, and finally destroys the object and it prints what it
    should.

    > You're not supposed to pass a temporary for a non-const reference
    > parameter (in this case, the first parameter of operator<<). The result
    > of doing so is undefined.


    Right, but for the last case it's fine.


    To the original problem, a possible solution is

    class Gatherer : public std::stringstream {
    public:

    ~Gatherer ( void ) {
    std::cout << d_stream.str() << std::endl;
    }
    template <class T>
    Gatherer& operator<<(const T&);

    private:
    std::stringstream d_stream;
    };
     
    Siemel Naran, Jun 11, 2004
    #6
  7. Kai-Uwe Bux

    Siemel Naran Guest

    "Victor Bazarov" <> wrote in message news:QX7yc.2123

    > I think you're running into synchronisation problem. When compiled
    > with Intel v4.5 or VC++ v6sp5, this code outputs three hello. When
    > compiled with VC++ v7.1, it prints an address first and then two
    > hello, just like you described.


    MSVC7is more right than Intel or MSVC7. The compiler should never call the
    non-member. The ideal compiler would give an error/warning.
     
    Siemel Naran, Jun 11, 2004
    #7
  8. Kai-Uwe Bux wrote:
    > Russell Hanneken wrote:
    >>
    >>You're not supposed to pass a temporary for a non-const reference
    >>parameter (in this case, the first parameter of operator<<). The result
    >>of doing so is undefined.

    >
    > Could you direct me to the point in the standard where I find this.


    I couldn't find a place where it's stated directly. I think it's more
    an implication of 8.5.3, section 5, which lists the valid ways to
    initialize a reference. Temporaries just don't meet the qualifications
    to be an initializer for a non-const reference.

    Bjarne Stroustrup puts it pretty bluntly on page 98 of _The C++
    Programming Language_: "The initializer for a 'plain' T& must be an
    lvalue of type T."

    --
    Russell Hanneken

    Use ROT13 to decode my email address.
     
    Russell Hanneken, Jun 11, 2004
    #8
  9. Kai-Uwe Bux

    Kai-Uwe Bux Guest

    Hi,


    Thank you very much for this detailed explanation. Walking me through
    the problem like that was more than I could have hoped for.


    Best

    Kai-Uwe
     
    Kai-Uwe Bux, Jun 11, 2004
    #9
  10. Siemel Naran wrote:
    >
    >> Gatherer() << std::dec << "hello world!";

    >
    > Also works as expected. Fortunately Gatherer() << std::dec calls a member
    > function
    >
    > basic_ostream::eek:perator<<(ios_base& (*)(ios_base&))
    >
    > and for member functions you are allowed to call non-const member functions
    > on a temporary. So this sets the decimal mode, returns an ostream&, adds
    > text to the ostream, and finally destroys the object and it prints what it
    > should.
    >
    >>You're not supposed to pass a temporary for a non-const reference
    >>parameter (in this case, the first parameter of operator<<). The result
    >>of doing so is undefined.

    >
    > Right, but for the last case it's fine.


    I don't follow this. The result of

    Gatherer() << std::dec

    is a reference to a temporary. So

    (result of first expression) << "hello world!"

    is an attempt to initialize a non-const reference parameter with a
    temporary, which is still a problem, right?

    --
    Russell Hanneken

    Use ROT13 to decode my email address.
     
    Russell Hanneken, Jun 11, 2004
    #10
  11. Kai-Uwe Bux

    tom_usenet Guest

    On Fri, 11 Jun 2004 16:35:16 GMT, Russell Hanneken <>
    wrote:

    >Siemel Naran wrote:
    >>
    >>> Gatherer() << std::dec << "hello world!";

    >>
    >> Also works as expected. Fortunately Gatherer() << std::dec calls a member
    >> function
    >>
    >> basic_ostream::eek:perator<<(ios_base& (*)(ios_base&))
    >>
    >> and for member functions you are allowed to call non-const member functions
    >> on a temporary. So this sets the decimal mode, returns an ostream&, adds
    >> text to the ostream, and finally destroys the object and it prints what it
    >> should.
    >>
    >>>You're not supposed to pass a temporary for a non-const reference
    >>>parameter (in this case, the first parameter of operator<<). The result
    >>>of doing so is undefined.

    >>
    >> Right, but for the last case it's fine.

    >
    >I don't follow this. The result of
    >
    > Gatherer() << std::dec
    >
    >is a reference to a temporary. So
    >
    > (result of first expression) << "hello world!"
    >
    >is an attempt to initialize a non-const reference parameter with a
    >temporary, which is still a problem, right?


    No. (result of first expression) is a std::eek:stream& that happens to
    reference a temporary. You can bind a std::eek:stream& to a std::eek:stream&
    no problem.

    Don't forget that the temporary lasts until the end of the full
    expression, usually the ;

    Tom
    --
    C++ FAQ: http://www.parashift.com/c -faq-lite/
    C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
     
    tom_usenet, Jun 11, 2004
    #11
  12. Kai-Uwe Bux

    Kai-Uwe Bux Guest

    tom_usenet wrote:

    > On Fri, 11 Jun 2004 16:35:16 GMT, Russell Hanneken <>
    > wrote:
    >
    >>Siemel Naran wrote:
    >>>
    >>>> Gatherer() << std::dec << "hello world!";
    >>>
    >>> Also works as expected. Fortunately Gatherer() << std::dec calls a
    >>> member function
    >>>
    >>> basic_ostream::eek:perator<<(ios_base& (*)(ios_base&))
    >>>
    >>> and for member functions you are allowed to call non-const member
    >>> functions
    >>> on a temporary. So this sets the decimal mode, returns an ostream&,
    >>> adds text to the ostream, and finally destroys the object and it prints
    >>> what it should.
    >>>
    >>>>You're not supposed to pass a temporary for a non-const reference
    >>>>parameter (in this case, the first parameter of operator<<). The result
    >>>>of doing so is undefined.
    >>>
    >>> Right, but for the last case it's fine.

    >>
    >>I don't follow this. The result of
    >>
    >> Gatherer() << std::dec
    >>
    >>is a reference to a temporary. So
    >>
    >> (result of first expression) << "hello world!"
    >>
    >>is an attempt to initialize a non-const reference parameter with a
    >>temporary, which is still a problem, right?

    >
    > No. (result of first expression) is a std::eek:stream& that happens to
    > reference a temporary. You can bind a std::eek:stream& to a std::eek:stream&
    > no problem.
    >
    > Don't forget that the temporary lasts until the end of the full
    > expression, usually the ;
    >
    > Tom


    Thanks to all of you. This is getting really interesting, and I have to
    wonder why C++ is imposing this restriction. Consider:


    #include <iostream>
    #include <sstream>

    class Gatherer : public std::stringstream {
    public:

    ~Gatherer ( void ) {
    std::cout << this->str() << std::endl;
    }

    void insert ( const std::string & str ) {
    *this << str;
    }

    Gatherer& me ( void ) {
    return( *this );
    }
    };

    static
    void insert ( Gatherer & gather, const std::string str ) {
    gather.insert( str );
    }

    int main ( void ) {
    insert( Gatherer(), std::string( "hello world!" ); // error
    insert( Gatherer().me(), std::string( "hello world!" ) ); // sound
    }

    From what I read in this thread, the second line should be sound because
    (a) I am allowed to call a non-const method of a temporary and (b) this
    method returns a reference that just happens to be bound to a temporary and
    therefore can be used to initialized the non-const & parameter in insert.
    Boy, what surprises C++ has to offer once you scratch a little bit on the
    surface.

    I have learned now that 8.5.3.5 is at the heart of the matter. It states
    that a reference is to be initialized by an lvalue or by a const
    non-volatile expression. Now there are two questions left:

    1) When the standard says that a program shall initialize references
    according to the rules in 8.5.3.5, does this mean that if a program fails
    to do so, we have undefined behaviour or does this count as a "diagnosable
    semantic rule" so that a compliant compiler will have to tell me.

    2) What is the rational for the rules in 8.5.3.5 in the first place. If
    they can be circumvented like above, they seem not to offer protection in
    the first place.


    Thanks again for all the explanations.


    Best

    Kai-Uwe
     
    Kai-Uwe Bux, Jun 11, 2004
    #12
  13. tom_usenet wrote:
    >
    > No. (result of first expression) is a std::eek:stream& that happens to
    > reference a temporary. You can bind a std::eek:stream& to a std::eek:stream&
    > no problem.


    But the reference is still to a temporary object. A temporary object isn't
    an lvalue, right? And doesn't a non-const reference have to be initialized
    by an lvalue?

    --
    Russell Hanneken

    Use ROT13 to decode my email address.
     
    Russell Hanneken, Jun 11, 2004
    #13
  14. Kai-Uwe Bux

    Kai-Uwe Bux Guest

    Russell Hanneken wrote:

    > tom_usenet wrote:
    >>
    >> No. (result of first expression) is a std::eek:stream& that happens to
    >> reference a temporary. You can bind a std::eek:stream& to a std::eek:stream&
    >> no problem.

    >
    > But the reference is still to a temporary object. A temporary object
    > isn't
    > an lvalue, right? And doesn't a non-const reference have to be
    > initialized by an lvalue?
    >


    From the standard 3.10.5:

    The result of calling a function that does not return a reference is an
    rvalue. User defined operators are functions, and whether such operators
    expect or yield lvalues is determined by their parameter and return types.


    Unfortunately, this does not tell us the precise rules how the parameter
    and return typed determine whether the result is an lvalue. However it
    seems to imply that it shall not matter whether the object whose member
    fucntion is called is a temporary--that would not show in the parameter and
    return types.


    Best

    Kai-Uwe
     
    Kai-Uwe Bux, Jun 11, 2004
    #14
  15. Kai-Uwe Bux

    Kai-Uwe Bux Guest

    Hi everybody,


    First I would like to thank everybody participating in this thread:
    I learned a lot.

    Out of pure curiosity, I whipped up some code that sums up the
    understanding of the situation that I have as of now. I would like
    to solicity any kind of criticism / code review. In particular, I
    would like to know if my reasoning for correctness of the code is
    flawed.


    Thanks again

    Kai-Uwe

    // How to pass a temporary to a non-const reference
    // ================================================

    // ============================================
    // | Description |
    // ============================================
    /*
    | This defines the template l_ctor<T> which promotes
    | constructors for T to effectively yield lvalues.
    |
    | A constructor call T() [ T(a), T(a,b), ... ] will
    | create a temporary object of type T. This cannot by
    | itself be used as an lvalue. In particular, it is not
    | suitable to initialize a T& parameter in a function call.
    | Sometimes, this might be a nuisance.
    |
    | The constructor l_ctor<T() [ l_ctor<T>(a),
    | l_ctor<T>(a,b), ... ] promotes the underlying
    | constructor T() so that it actually yields a
    | something that at least can be converted to an
    | lvalue and thus used to initialize a T&.
    */

    // ============================================
    // | Implementation |
    // ============================================

    // The template class l_ctor<T>
    // ----------------------------
    /*
    | We use reversed inheritance.
    | I just learned this two days ago through an introduction
    | to templates that was criticized on this news group.
    | I have to admit, I was not sure whether reversed inherticance
    | could be useful.
    */
    template < typename T >
    class l_ctor : public T {
    public:

    l_ctor ( void ) :
    T ()
    {}

    template < typename A >
    l_ctor ( A a ) :
    T ( a )
    {}

    template < typename A, typename B >
    l_ctor ( A a, B b ) :
    T ( a, b )
    {}

    template < typename A, typename B, typename C >
    l_ctor ( A a, B b, C c ) :
    T ( a, b, c )
    {}

    template < typename A, typename B, typename C,
    typename D >
    l_ctor ( A a, B b, C c, D d ) :
    T ( a, b, c, d )
    {}

    template < typename A, typename B, typename C,
    typename D, typename E >
    l_ctor ( A a, B b, C c, D d, E e ) :
    T ( a, b, c, d, e )
    {}

    template < typename A, typename B, typename C,
    typename D, typename E, typename F >
    l_ctor ( A a, B b, C c, D d, E e, F f ) :
    T ( a, b, c, d, e, f )
    {}

    // Now we add a feature [maybe a bug]:
    // This class can be converted to T&:
    operator T& ( void ) {
    return( *this );
    }

    }; // l_ctor<T>


    // ===========================================
    // | How and why it works: |
    // ===========================================
    /*
    | In calling l_ctor<T>() a temporary onject of type l_ctor<T>
    | is created. The templated constructor invokes T() to initialize
    | the temporary. This temporary is convertible to T&, which would
    | be an lvalue. Thus, this object satisfies the requirements of
    | 8.5.3.5 and can be used to initialize a T&.
    */


    // ===========================================
    // | Example |
    // ===========================================

    #include <iostream>
    #include <string>

    void write ( std::eek:stream & out, std::string line ) {
    out << line;
    }

    int main ( void ) {
    write( l_ctor<std::eek:stream>( std::cout.rdbuf() ), "hello world!\n" );
    }

    // end of story
     
    Kai-Uwe Bux, Jun 12, 2004
    #15
  16. Kai-Uwe Bux

    Siemel Naran Guest

    "Russell Hanneken" <> wrote in message news:8blyc.10340

    > I don't follow this. The result of
    >
    > Gatherer() << std::dec
    >
    > is a reference to a temporary. So


    Yes, it is a reference to a temporary, but the standard allows it just
    because this version of operator<< happens to be a member function. Also,
    the standard requires the compiler only destroy the temporary object at the
    end of the statement or the semicolon (unless it can prove that destructing
    the object earlier won't have side effects, the as-if rule).

    So now that you've returned the temporary, you're free to use in any
    operator<<, whether it is a member or non-member function. The invocation
    of the second operator<< doesn't itself create a temporary, but instead it
    uses the reference returned by the first operator<<, so this second
    operator<< can be a member or non-member function.

    > (result of first expression) << "hello world!"


    The whole thing is

    operator<<(operator<<(Gatherer(), std::dec), "hello world!");

    As written, the inner operator<< is illegal because you pass a temporary by
    non-const reference. But because this particular operator<< happens to be a
    member function, the compiler must suppress the error message.

    So then it does create a temporary, which it will destroy at the semicolon,
    after completing the second outer operator<<.

    And so the inner operator<< basically returns a reference to this temporary.

    But to the outer operator<< all you have is a reference to an existing
    ostream (which happens to be a temporary). But it does not care about
    whether the original object was a temporary or not. So it goes ahead and
    uses the reference to non-const ostream& in the call to print "hello
    world!".

    It seems like a flaw in the language that you can get different behavior
    depending on whether the overloaded operator is a member or non-member.
    It's only because of the mix between operator overloading, license to use
    members or non-members, and function overloading -- ie. you can overload
    operator<< either as a member or non-member, and then have different
    operator<< for different arguments. Maybe a good compiler will compensate
    for the flaw by throwing a warning message.


    > is an attempt to initialize a non-const reference parameter with a
    > temporary, which is still a problem, right?
     
    Siemel Naran, Jun 12, 2004
    #16
  17. Siemel Naran wrote:
    > "Russell Hanneken" <> wrote in message news:8blyc.10340
    >
    >>I don't follow this. The result of
    >>
    >> Gatherer() << std::dec
    >>
    >>is a reference to a temporary. So

    >
    > Yes, it is a reference to a temporary, but the standard allows it just
    > because this version of operator<< happens to be a member function.


    You seem to think I'm saying that

    Gatherer()

    results in a temporary, and that I have a problem with the result being
    used as the left operand in the above << operation. No, I'm saying that

    Gatherer() << std::dec

    results in a reference to a temporary object, and I have a problem with
    that result being used in the larger << operation.

    > the standard requires the compiler only destroy the temporary object at the
    > end of the statement or the semicolon (unless it can prove that destructing
    > the object earlier won't have side effects, the as-if rule).


    This is neither here nor there. We all agree that this statement

    Gatherer() << "hello world!";

    is bad, despite the fact that the temporary object produced by
    Gatherer() exists until the end of the statement.

    > So now that you've returned the temporary, you're free to use in any
    > operator<<, whether it is a member or non-member function. The invocation
    > of the second operator<< doesn't itself create a temporary, but instead it
    > uses the reference returned by the first operator<<, so this second
    > operator<< can be a member or non-member function.


    This is exactly the point I don't see. Where does the standard say
    that? What difference does it make whether the temporary object comes
    directly from

    Gatherer()

    or comes indirectly from

    Gatherer() << std::dec

    ? It's the same temporary object. And as far as I know, it's illegal
    to use temporary objects to initialize non-const references. A
    temporary object isn't an lvalue.

    > operator<<(operator<<(Gatherer(), std::dec), "hello world!");
    >

    [. . .]
    >
    > And so the inner operator<< basically returns a reference to this temporary.
    >
    > But to the outer operator<< all you have is a reference to an existing
    > ostream (which happens to be a temporary). But it does not care about
    > whether the original object was a temporary or not.


    Why not?

    --
    Russell Hanneken

    Use ROT13 to decode my email address.
     
    Russell Hanneken, Jun 12, 2004
    #17
  18. Kai-Uwe Bux

    Siemel Naran Guest

    "Russell Hanneken" <> wrote in message
    news:VHHyc.2708$...
    > Siemel Naran wrote:


    > This is exactly the point I don't see. Where does the standard say
    > that? What difference does it make whether the temporary object comes
    > directly from
    >
    > Gatherer()
    >
    > or comes indirectly from
    >
    > Gatherer() << std::dec
    >
    > ? It's the same temporary object. And as far as I know, it's illegal
    > to use temporary objects to initialize non-const references. A
    > temporary object isn't an lvalue.


    Good question. It's because the standard does not require the compiler to
    analyze the origin of variables. In operator<<(Gatherer(),whatever) the
    compiler can see the first argument is a temporary. In operator<<(something
    that could perhaps be a temporary, whatever) the compiler does not know
    whether the first argument is a temporary, because it does not analyze where
    the object came from. It sounds strange, and it is.

    With the analysis that you seem to propose, the compiler should analyze the
    origin of the reference, to determine whether it really is a temporary.
    This kind of analysis would be good for const_cast too. The compiler never
    gives an error if you cast away const, but if it does analysis to determine
    if the original object were declared const in the first place, then it could
    give an error.

    But consider this:

    ostream& silly(ostream& o) {
    o << "hello world!";
    }

    Gatherer() << silly; // works because this particular operator<< is a
    non-member

    Now in function silly the compiler is calling a non-member function
    operator<<(ostream&, const char *) on a temporary. Yet it won't error out.
    Pretty strange stuff!
     
    Siemel Naran, Jun 12, 2004
    #18
  19. Kai-Uwe Bux

    tom_usenet Guest

    On Fri, 11 Jun 2004 14:37:55 -0400, Kai-Uwe Bux <>
    wrote:

    >I have learned now that 8.5.3.5 is at the heart of the matter. It states
    >that a reference is to be initialized by an lvalue or by a const
    >non-volatile expression. Now there are two questions left:
    >
    >1) When the standard says that a program shall initialize references
    >according to the rules in 8.5.3.5, does this mean that if a program fails
    >to do so, we have undefined behaviour or does this count as a "diagnosable
    >semantic rule" so that a compliant compiler will have to tell me.


    "shall" generally refers to a diagnosable rule. Undefined behaviour is
    explicitly indicated where appropriate.

    >2) What is the rational for the rules in 8.5.3.5 in the first place. If
    >they can be circumvented like above, they seem not to offer protection in
    >the first place.


    It's to avoid accidental errors. e.g. if it were allowed:

    void f(int& i)
    {
    i = 10;
    }

    float val;

    f(val);
    //val unchanged!

    For classes you might get a similar problem with conversion functions.
    e.g.

    void f(std::string& s)
    {
    s = "Foo";
    }

    f("Hello"); //whoops

    If you explicitly get hold of a non-const reference, then that's up to
    you - you know what you're doing. You may find this useful:

    template <class T>
    T& rvalue_to_lvalue(T const& t)
    {
    return const_cast<T&>(t);
    }

    Obviously it must be used with care, and never called on a const
    value.

    The next C++ standard is considering a feature called "rvalue
    references", which can bind to temporaries:

    std::string&& s = get_temp_string();

    They are useful for implementing "move semantics", and area where C++
    is currently rather weak.

    Tom
    --
    C++ FAQ: http://www.parashift.com/c -faq-lite/
    C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
     
    tom_usenet, Jun 14, 2004
    #19
  20. Kai-Uwe Bux

    tom_usenet Guest

    On Fri, 11 Jun 2004 19:44:25 GMT, "Russell Hanneken" <>
    wrote:

    >tom_usenet wrote:
    >>
    >> No. (result of first expression) is a std::eek:stream& that happens to
    >> reference a temporary. You can bind a std::eek:stream& to a std::eek:stream&
    >> no problem.

    >
    >But the reference is still to a temporary object. A temporary object isn't
    >an lvalue, right?


    No, but a reference to one is. There are plenty of ways of forming an
    lvalue from a temporary if you so wish. All the language forbids is
    the direct binding of an rvalue to a non-const reference.

    Tom
    --
    C++ FAQ: http://www.parashift.com/c -faq-lite/
    C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
     
    tom_usenet, Jun 14, 2004
    #20
    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. Marc Schellens

    stringstream

    Marc Schellens, Jul 1, 2003, in forum: C++
    Replies:
    3
    Views:
    532
  2. minjie
    Replies:
    1
    Views:
    662
    Victor Bazarov
    Jul 15, 2003
  3. Replies:
    9
    Views:
    537
    CBFalconer
    Apr 25, 2006
  4. Replies:
    7
    Views:
    3,217
    James Kanze
    Feb 12, 2008
  5. K. Frank
    Replies:
    20
    Views:
    1,297
    Howard Hinnant
    Mar 28, 2011
Loading...

Share This Page