Temporary objects as constructor arguments?

Discussion in 'C++' started by Andy Buckley, Aug 25, 2004.

  1. Andy Buckley

    Andy Buckley Guest

    Hi,

    A friend and I have recently had trouble getting code to compile when
    using temporary objects in constructors. A minimal example is below:

    This code fragment is used to construct seven ROD objects and run
    their doSomething() method (which is trivial).

    As far as we can tell, all seven methods should successfully construct a
    ROD object using temporary objects as constructor parameters, but using
    g++ 3.2.3 only the first 4 actually succeed. The remainder seem to be
    treated as constructing/declaring(?) a function pointer and so the attempt
    to call a method fails for these.

    Confusingly, some of the approaches which fail under g++ do compile
    successfully using MicroSoft Visual C++ 6.0: can anyone enlighten us as to
    whether this is a failing in our understanding of the language or a bug in
    g++? (probably the first!)



    // Minimal test case:

    #include <iostream>

    typedef unsigned int U;

    class T {
    public:
    T() {};
    T( const U & ) { }
    };

    class ROD {
    public:
    ROD(const T &) { }
    void doSomething() const {
    std::cout << "Hello -- I am a ROD" << std::endl;
    }
    };

    class CC {
    public:
    void doSomethingElse() {

    ROD rod1 = ROD( T( t() ) ) ; // does what I want -- makes a ROD
    ROD rod2 ( T( this->t() ) ) ; // does what I want -- makes a ROD
    ROD rod3 ( *(new T() ) ) ; // does what I want -- makes a ROD
    ROD rod4 ( T( 0 ) ) ; // does what I want -- makes a ROD

    rod1.doSomething(); // succeeds rod2.doSomething();
    // succeeds rod3.doSomething(); // succeeds
    rod4.doSomething(); // succeeds

    /*
    ROD rod5 ( T( t() ) ) ; // doesn't do what I want (*) ROD
    rod6 ( ROD( T( t() ) )); // doesn't do what I want (*) ROD
    rod7 ( T() ) ; // doesn't do what I want (*)

    rod5.doSomething(); // doesn't compile
    rod6.doSomething(); // doesn't compile
    rod7.doSomething(); // doesn't compile
    */

    // (*) At these statements, funtion pointers seem to be declared :(
    }
    T t() const { return T(0); }
    };

    int main () {
    CC cc;
    cc.doSomethingElse();
    return 0;
    }
     
    Andy Buckley, Aug 25, 2004
    #1
    1. Advertising

  2. Andy Buckley wrote:
    > A friend and I have recently had trouble getting code to compile when
    > using temporary objects in constructors. A minimal example is below:
    >
    > This code fragment is used to construct seven ROD objects and run
    > their doSomething() method (which is trivial).
    >
    > As far as we can tell, all seven methods should successfully construct a
    > ROD object using temporary objects as constructor parameters, but using
    > g++ 3.2.3 only the first 4 actually succeed. The remainder seem to be
    > treated as constructing/declaring(?) a function pointer and so the attempt
    > to call a method fails for these.


    The last two are not declarations at all. The fifth is a declaration.
    See below.

    > Confusingly, some of the approaches which fail under g++ do compile
    > successfully using MicroSoft Visual C++ 6.0: can anyone enlighten us as to
    > whether this is a failing in our understanding of the language or a bug in
    > g++? (probably the first!)


    It's a bug in your understanding of the language.

    >
    >
    >
    > // Minimal test case:
    >
    > #include <iostream>
    >
    > typedef unsigned int U;
    >
    > class T {
    > public:
    > T() {};
    > T( const U & ) { }
    > };
    >
    > class ROD {
    > public:
    > ROD(const T &) { }
    > void doSomething() const {
    > std::cout << "Hello -- I am a ROD" << std::endl;
    > }
    > };
    >
    > class CC {
    > public:
    > void doSomethingElse() {
    >
    > ROD rod1 = ROD( T( t() ) ) ; // does what I want -- makes a ROD
    > ROD rod2 ( T( this->t() ) ) ; // does what I want -- makes a ROD
    > ROD rod3 ( *(new T() ) ) ; // does what I want -- makes a ROD
    > ROD rod4 ( T( 0 ) ) ; // does what I want -- makes a ROD
    >
    > rod1.doSomething(); // succeeds rod2.doSomething();
    > // succeeds rod3.doSomething(); // succeeds
    > rod4.doSomething(); // succeeds
    >
    > /*
    > ROD rod5 ( T( t() ) ) ; // doesn't do what I want (*) ROD


    It's a declaration. See FAQ 10.2 and search the Google Groups for
    similar problems people have been asking about since Adam.

    > rod6 ( ROD( T( t() ) )); // doesn't do what I want (*) ROD


    Huh? You begin a statement with it, but 'rod6' is undefined.

    > rod7 ( T() ) ; // doesn't do what I want (*)


    'rod7' is undefined as well.

    Did you miss "ROD " in front of the two last statements? It seems
    to suggest that instead of copying and pasting the code from your
    C++ source file, you typed it in. Who knows how many more mistakes
    you've made while doing that...

    >
    > rod5.doSomething(); // doesn't compile
    > rod6.doSomething(); // doesn't compile
    > rod7.doSomething(); // doesn't compile
    > */
    >
    > // (*) At these statements, funtion pointers seem to be declared :(
    > }
    > T t() const { return T(0); }
    > };
    >
    > int main () {
    > CC cc;
    > cc.doSomethingElse();
    > return 0;
    > }


    V
     
    Victor Bazarov, Aug 25, 2004
    #2
    1. Advertising

  3. Andy Buckley

    Andy Buckley Guest

    On Wed, 25 Aug 2004 18:27:23 +0100, Victor Bazarov wrote:

    > Andy Buckley wrote:


    >> As far as we can tell, all seven methods should successfully construct
    >> a ROD object using temporary objects as constructor parameters, but
    >> using g++ 3.2.3 only the first 4 actually succeed. The remainder seem
    >> to be treated as constructing/declaring(?) a function pointer and so
    >> the attempt to call a method fails for these.

    >
    > The last two are not declarations at all. The fifth is a declaration.


    >> ROD rod5 ( T( t() ) ) ; // doesn't work

    >
    > It's a declaration. See FAQ 10.2 and search the Google Groups for
    > similar problems people have been asking about since Adam.


    Have looked at FAQ 10.2 and while rod5 is definitely being treated as a
    declaration of a function which returns a ROD, I'm confies as to why:
    the parentheses contain a real (if temporary) instantiation of a T object
    rather than a type declaration. FAQ 10.2 only seems to describe the case
    analogous to saying

    ROD rod5();

    which I can understand as declaring a function. Equivalently,

    ROD rod5( T t( U u() ) );

    according to the C++ function declaration grammar matches the definition
    of a set of nested function declarations (however crazy the idea might
    be). But when t() should return a concrete instatiation of a T object, I
    would have expected this to fail to match the function declaration.

    A clearer repost of the code without the copy-construction and
    with the erroneous line-wrapping hopefully sorted is below. If you can
    spare a moment to explain just what's going on, in particular on the
    highlighted line, we'd both be very happy! Thanks.


    // Reposted code:

    #include <iostream>

    typedef unsigned int U;

    class T {
    public:
    T( const U & ) { }
    };

    class ROD {
    public:
    ROD(const T &) { }
    void doSomething() const {
    std::cout << "Hello -- I am a ROD" << std::endl;
    }
    };

    class CC {
    public:
    void doSomethingElse() {

    ROD rod5( T( u() ) ); // *** line of interest
    rod5.doSomething(); // doesn't compile since it
    // thinks rod5 is a function ptr
    // of type ROD ()(T (*)())
    }

    U u() const { return 0; }
    };

    int main () {
    CC cc;
    cc.doSomethingElse();
    return 0;
    }
     
    Andy Buckley, Aug 25, 2004
    #3
  4. Andy Buckley wrote:
    > On Wed, 25 Aug 2004 18:27:23 +0100, Victor Bazarov wrote:
    >
    >
    >>Andy Buckley wrote:

    >
    >
    >>>As far as we can tell, all seven methods should successfully construct
    >>>a ROD object using temporary objects as constructor parameters, but
    >>>using g++ 3.2.3 only the first 4 actually succeed. The remainder seem
    >>>to be treated as constructing/declaring(?) a function pointer and so
    >>>the attempt to call a method fails for these.

    >>
    >>The last two are not declarations at all. The fifth is a declaration.

    >
    >
    >>> ROD rod5 ( T( t() ) ) ; // doesn't work

    >>
    >>It's a declaration. See FAQ 10.2 and search the Google Groups for
    >>similar problems people have been asking about since Adam.

    >
    >
    > Have looked at FAQ 10.2 and while rod5 is definitely being treated as a
    > declaration of a function which returns a ROD, I'm confies as to why:
    > the parentheses contain a real (if temporary) instantiation of a T object
    > rather than a type declaration. FAQ 10.2 only seems to describe the case
    > analogous to saying
    >
    > ROD rod5();
    >
    > which I can understand as declaring a function. Equivalently,
    >
    > ROD rod5( T t( U u() ) );
    >
    > according to the C++ function declaration grammar matches the definition
    > of a set of nested function declarations (however crazy the idea might
    > be). But when t() should return a concrete instatiation of a T object,


    Why?

    K k(blah)

    is either an object declaration or a function declaration depending on
    what 'blah' is. If 'blah' is another declaration (or is empty), then 'k'
    is a function that takes one argument (or nothing) and returns K.

    > I
    > would have expected this to fail to match the function declaration.


    Well, that's usually due to one's lack of experience with declarations.
    No big deal, we are all learning.

    >
    > A clearer repost of the code without the copy-construction and
    > with the erroneous line-wrapping hopefully sorted is below. If you can
    > spare a moment to explain just what's going on, in particular on the
    > highlighted line, we'd both be very happy! Thanks.
    >
    >
    > // Reposted code:
    >
    > #include <iostream>
    >
    > typedef unsigned int U;
    >
    > class T {
    > public:
    > T( const U & ) { }
    > };
    >
    > class ROD {
    > public:
    > ROD(const T &) { }
    > void doSomething() const {
    > std::cout << "Hello -- I am a ROD" << std::endl;
    > }
    > };
    >
    > class CC {
    > public:
    > void doSomethingElse() {
    >
    > ROD rod5( T( u() ) ); // *** line of interest
    > rod5.doSomething(); // doesn't compile since it
    > // thinks rod5 is a function ptr
    > // of type ROD ()(T (*)())


    Yes, that's the behaviour according to the Standard.

    First of all, anything that can be interpreted as a declaration, shall
    be interpreted as a declaration. That's the rule. It resolves ambiguity
    that otherwise exists in these particular cases.

    Second, if it's a declaration, how do you interpret it? Begin from what
    looks like a variable name, 'rod5'. It has a parenthesis right next to
    it. If what's inside the parentheses is not an expressions, it is very
    likely another declaration, then 'rod5' is a function. So, let's try to
    interpret what's inside the parentheses.

    You seem to understand the FAQ 10.2, so, I'll build my explanation based
    on that. Imagine that instead of

    int a;

    you have

    int (a);

    Does it change anything? No. The parentheses surrounding the variable
    name are superfluous in that case, and change nothing. So

    int (a);

    is equivalent to

    int a;

    .. If that's so, then I can always remove the parentheses that follow the
    type name and the declaration will mean the same, right? Now, if I say

    int(a());

    what is it? Nothing else but

    int a();

    (after removing the top-level parentheses). Now replace 'int' with 'T'
    and 'a' with 'u'. What do you get?

    T(u());

    What is it? A declaration of 'u', that is a function that takes no args
    and returns an value of type T. Now simply put that declaration inside
    the parentheses of a function declaration and you get

    ROD rod5(T(u()));

    which is equivalent to

    ROD rod5( T u() );

    which means 'rod5' is a function that takes one argument of type [pointer
    to function that takes no arguments and returns T] and returns ROD.

    * * *

    Now, the question is, "how do we fix this"? This is one way:

    ROD rod5 = ROD(T(u()));

    which would generate an error because 'u' is undefined. You probably
    meant

    ROD rod5 = ROD(T(U()));

    Another way is to use real objects of type U and T:

    U u;
    T t(u);
    ROD rod5(t);

    > }
    >
    > U u() const { return 0; }
    > };
    >
    > int main () {
    > CC cc;
    > cc.doSomethingElse();
    > return 0;
    > }


    Victor
     
    Victor Bazarov, Aug 25, 2004
    #4
  5. Hi Victor,

    (by the way, I'm the friend Andy referred to)

    First I'd like to thank you for your most recent and fullest answer
    to this thread. It has been very helpful in clarifying our original
    misunderstanding, and I've definitely learned some things.

    If nothing else happens to this thread I will consider it usefully
    closed.

    Nevertheless, I don't think any harm will come if I try to paraphrase
    what you have already said (a) to make sure that we have understood
    everything correctly, and (b) to check that a small (almost
    throw-away) remark you made near the foot of the last message is
    indeed nothing more than a simple slip -- and not something more
    important.

    So here goes my paraphrasing:

    The first and most important thing you said (I'll call it (1)) is:

    > First of all, anything that can be interpreted as a declaration,
    > shall be interpreted as a declaration. That's the rule. It
    > resolves ambiguity that otherwise exists in these particular cases.


    The above comment particularly relevant to the line I have
    labelled (3) below:

    class CC {
    void doSomething();
    U u(); // (2)
    };
    void CC::doSomething() {
    ROD rod5(T(u())); // (3)
    };

    which was found within the definition of a method of a class CC that
    ALSO DEFINED A METHOD u() (labelled (2) above). [ In the above
    fragment I have separated the definition of the doSomething method
    from its declaration for reasons that will hopefully become clearer
    shortly. ]

    The other thing you carefuly explained (but which I don't copy here)
    was to explain how a line like (3) could correctly be interpreted IN
    THE ABSENCE of a declaration such as (2) as a function declaration.

    Our original mistake was to believe that the declaration of u() in
    (2) would (within the scope of definitions of methods of CC such as
    in location (3)) be sufficient to prevent it later being interpreted
    as a declaration for something else.

    Your comment (1) tells us WHY we were wrong:

    Your comment (1) tells us that although (2) declared our "intention"
    (whatever that means!) for how we wanted "u()" to be interpreted, the
    mere fact that it COULD be interpreted in (3) as a function
    declaration was sufficient to REQUIRE it to be interpreted so. Fair
    enough. There must, of course, be a rule.

    So if everything I have said above is correct, then I am now a happy
    man as I now understand the language a bit better!

    Thanks Victor.

    I only re-iterate the above because of the throw-away remark I
    alluded to earlier that we found at the end of your last mail which
    casts just a tiny doubt in my mind, but which probably signifies
    nothing much:

    > Now, the question is, "how do we fix this"? This is one way:
    > ROD rod5 = ROD(T(u())); // (4)
    > which would generate an error because 'u' is undefined.


    Now you can see why I (in this posting) separated the implementation
    of CC::doSomething() from its declaration in the class definition --
    because this way I think it's easier to see that 'u' (or at least
    CC::u) really IS defined at the point at which the compiler tries to
    attack the internals of doSomething. Or at least we intended it to
    be defined! As far as I know, the status of the definition of CC::u
    would not be affected by moving the implementation of doSomething to
    within the class body (even if doSomthing happened to be declared
    ahead of u()), and so I presume that what I've said here also applies
    equally to what we expected from our last posting.

    So I'm going to assume that you simply didn't spot the u()
    declaration in our CC class (an understandable mistake to make given
    how we managed to mess up the original posting in more than one
    way!), and that as a result the status of (4) moves from failing to
    fix the problem, to actually being a good problem fix!

    Which is a good thing!

    So again, thank you for your time.

    Christopher
     
    Christopher Lester, Aug 25, 2004
    #5
  6. "Christopher Lester" <> wrote...
    > [...]


    You got it right.

    > I only re-iterate the above because of the throw-away remark I
    > alluded to earlier that we found at the end of your last mail which
    > casts just a tiny doubt in my mind, but which probably signifies
    > nothing much:
    >
    > > Now, the question is, "how do we fix this"? This is one way:
    > > ROD rod5 = ROD(T(u())); // (4)
    > > which would generate an error because 'u' is undefined.

    >
    > Now you can see why I (in this posting) separated the implementation
    > of CC::doSomething() from its declaration in the class definition --
    > because this way I think it's easier to see that 'u' (or at least
    > CC::u) really IS defined at the point at which the compiler tries to
    > attack the internals of doSomething. Or at least we intended it to
    > be defined! As far as I know, the status of the definition of CC::u
    > would not be affected by moving the implementation of doSomething to
    > within the class body (even if doSomthing happened to be declared
    > ahead of u()), and so I presume that what I've said here also applies
    > equally to what we expected from our last posting.
    >
    > So I'm going to assume that you simply didn't spot the u()
    > declaration in our CC class (an understandable mistake to make given
    > how we managed to mess up the original posting in more than one
    > way!), and that as a result the status of (4) moves from failing to
    > fix the problem, to actually being a good problem fix!


    That's the ticket. I didn't pay too close attention to the fact
    that 'u' was another member function. I thought (incorrectly) that
    "u()" was a slip-up by Andy, who meant to write "U()".

    >
    > Which is a good thing!


    Depends on how you look at it, don't it?

    V
     
    Victor Bazarov, Aug 26, 2004
    #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. Replies:
    5
    Views:
    356
    Victor Bazarov
    Jun 24, 2005
  2. Replies:
    7
    Views:
    1,409
    Jakob Bieling
    Jul 21, 2005
  3. Generic Usenet Account
    Replies:
    10
    Views:
    2,248
  4. Replies:
    7
    Views:
    3,227
    James Kanze
    Feb 12, 2008
  5. Marcelo De Brito

    Copy Constructor And Temporary Objects

    Marcelo De Brito, Mar 1, 2009, in forum: C++
    Replies:
    2
    Views:
    502
    Kai-Uwe Bux
    Mar 1, 2009
Loading...

Share This Page