Thread: Function argument not converted to reference

  1. #1
    Registered User
    Join Date
    Oct 2006
    Posts
    250

    Function argument not converted to reference

    The following piece of code is causing troubles for me

    Code:
    class Vector
    {
    	public:
    		Vector operator- (Vector &v)
    		{
    			Vector w;
    	
    			w.x = x - v.x;
    			w.y = y - v.y;
    			w.z = z - v.z;
    			
    			return w;
    		};
    		
    		static Vector cross(Vector &u, Vector &v)
    		{
    			Vector w;
    			
    			w.x = u.y * v.z - u.z * v.y;
    			w.y = u.z * v.x - u.x * v.z;
    			w.z = u.x * v.y - u.y * v.x;
    			
    			return w;
    		};
    		
    		float x, y, z;
    };
    
    typedef struct
    {
    	Vector	x;
    	Vector	v, omega;
    } RigidBody;
    
    Vector
    pt_velocity(RigidBody &body, Vector &p)
    {
    	return body.v + Vector::cross(body.omega, p - body.x));
    }
    Whenever I try to compile this (g++ -c -o test.o test.cpp), I get the following error:
    test.cpp: In function ‘Vector pt_velocity(RigidBody&, Vector&)’:
    test.cpp:38: error: no matching function for call to ‘Vector::cross(Vector&, Vector)’
    test.cpp:15: note: candidates are: static Vector Vector::cross(Vector&, Vector&)

    As the second argument of Vector::cross is supposed to be a 'Vector &', shouldn't the compiler automatically pass the 'Vector' returned by 'operator-' as a 'Vector &'? What is the cause of this error?

    The compiler I am using is g++ version 4.0.3.
    Last edited by MWAAAHAAA; 10-01-2006 at 10:37 AM.

  2. #2
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,613
    Static member functions are only good working with static member data. Remember that static member functions can be called when no Vectors exist. So my suggestion is to remove the static keyword, as it is being used wrong here.

  3. #3
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Make the references const; you can't pass the unnamed temporary object as a non-const reference.

    And I think the static for that particular function is fine -- it's accessing member data only of the vectors passed as parameters, so it's OK.
    Last edited by Cat; 10-01-2006 at 11:10 AM.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  4. #4
    Registered User
    Join Date
    Mar 2005
    Posts
    135
    You haven't defined operator +

    - xeddiex

  5. #5
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    You obviously know why this is happening. As you well said, the return of the overloaded subtraction operator is a Vector object, not a reference to Vector.

    But the exact reason is buried within the way references work when used as parameters of a function.

    When you use a plain reference as a parameter to a function, you must be aware that the argument used may not be an rvalue or any value that requires a conversion. The overloaded subtraction operator returns an rvalue. This breaks the ability to be used as an argument to the cross function.

    You have two options. Either you return a reference from the overloaded subtraction operator, or you alter the parameters to const references.

    Which one you choose is up to what you want to do with them. But probably you should do both.

    Unless you want to overloaded subtraction to be exclusively a binary operation (Vector - Vector is ok, but Vector - Vector - Vector is not), it should return a reference to a Vector object. This has the advantage of returning an lvalue instead of just an rvalue.

    As for the cross function, the idea with reference parameter is to always make them const references when you are not going to change their values. This is so because as you can see const references are way more flexible than non const ones.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  6. #6
    Registered User
    Join Date
    Mar 2005
    Posts
    135
    He also has a syntax error on this line:
    return body.v + Vector::cross(body.omega, p - body.x));

    - xeddiex

  7. #7
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    It's just an extra ) that he probably has already caught by now
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  8. #8
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Quote Originally Posted by Mario F.
    Unless you want to overloaded subtraction to be exclusively a binary operation (Vector - Vector is ok, but Vector - Vector - Vector is not), it should return a reference to a Vector object. This has the advantage of returning an lvalue instead of just an rvalue.
    I'm not sure exactly how you'd return a reference in an operator-. You can't return a reference to a local variable after all.

    And you can chain operator- without needing a reference; you really need references for things like operator= to chain them. But vector1 - vector2 - vector3 will certainly work even returning by value.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  9. #9
    Registered User
    Join Date
    Oct 2006
    Posts
    250
    Thanks Mario F. and Cat. I didn't know about this property of const references. Your solution actually works. Besides, it is more appropriate to have the parameters of these particular functions being const, as they are not supposed to be modified anyway.

    Yes, xeddiex, there was a syntax error, and I didn't post my definition of operator+. Sorry for the omissions, but this piece of code was part of a larger program. As a side note, I ran into the same problem with operator+ before I changed its arguments to const as well.

    As for returning a reference from the function. The way I see it this is simply not possible, as the variable 'w' is local to the function. Returning a reference to this local variable would result in an invalid reference outside of this function, or am I getting something wrong here?

  10. #10
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    Yes. You are correct. Besides it would be more consistent with the built-in subtraction operator.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  11. #11
    Registered User
    Join Date
    Mar 2005
    Posts
    135
    Quote Originally Posted by Mario F.
    You obviously know why this is happening. As you well said, the return of the overloaded subtraction operator is a Vector object, not a reference to Vector.

    But the exact reason is buried within the way references work when used as parameters of a function.


    When you use a plain reference as a parameter to a function, you must be aware that the argument used may not be an rvalue or any value that requires a conversion. The overloaded subtraction operator returns an rvalue. This breaks the ability to be used as an argument to the cross function.


    You have two options. Either you return a reference from the overloaded subtraction operator, or you alter the parameters to const references.

    Which one you choose is up to what you want to do with them. But probably you should do both.

    Unless you want to overloaded subtraction to be exclusively a binary operation (Vector - Vector is ok, but Vector - Vector - Vector is not), it should return a reference to a Vector object. This has the advantage of returning an lvalue instead of just an rvalue.

    As for the cross function, the idea with reference parameter is to always make them const references when you are not going to change their values. This is so because as you can see const references are way more flexible than non const ones.
    Mario, I have a question about the above boldfaced paragraph: operator -, returns by value as we all know, but, after the return statement, a unnamed temporary is created W/the value of the return statment and this results in an rvalue. I don't see how a non-const argument, would not work - problem/error free - in the OP's code because a temporary is still a valid object in that it *is* useable as an rvalue and lvalue *until* the end of the semicolon of the statement where vector::cross is called.

    The reason, as I have come to know, why temporary rvalue objects should be treated as an rvalue only and marked as const, is because they only exist for a short period and once the end of the statement (or sequence point) is reached where the temporary object was created, then, the object will be destroyed. So I don't see the OP's code, without the constness argument, problematic.

    So my question to you is: tell me why I should not go with what I just said. Am I wrong, or there's more to it that I haven't yet grasped?

    Thanks,
    - xeddiex
    Last edited by xeddiex; 10-01-2006 at 12:59 PM.

  12. #12
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Quote Originally Posted by xeddiex
    I don't see how a non-const argument, would not work - problem/error free - in the OP's code because a temporary is still a valid object in that it *is* useable as an rvalue and lvalue *until* the end of the semicolon of the statement where vector::cross is called.
    An unnamed temporary can never be used as an lvalue, only an rvalue. It's still an object, yes, but it's not a valid lvalue.

    For example, say you have this:

    Code:
       MyClass::MyClass(int){}
    
       MyClass myFunction();
    
    ...
    
       myFunction() = MyClass(2);
    In that code, you're trying to use the temporary, unnamed object returned by myFunction as an lvalue, and no C++ compiler will permit it. You can still of course do:


    Code:
       MyClass::MyClass(int){}
    
       MyClass myFunction();
    
    ...
       MyClass m = myFunction();
       m = MyClass(2);
    which achieves the same effect, but is syntactically correct because the return value of myFunction is (correctly) used only as an rvalue, not an lvalue.

    If the original code had returned a reference (a legal lvalue) then the original code would be acceptable.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  13. #13
    (?<!re)tired Mario F.'s Avatar
    Join Date
    May 2006
    Location
    Ireland
    Posts
    8,446
    First, just for clarification, there is a third reason for the parameter to be made const. Non const parameters cannot be used for const arguments. So we again needlessly restrict the use of the function. As a rule of thumb, the keyword const should be used proactively all over our code.

    But to answer your question, I'm sure others will be able to give you a better explanation. But this is my try:


    > operator -, returns by value as we all know, but, after the return statement, a unnamed temporary is created W/the value of the return statment and this results in an rvalue.

    Correct.

    > I don't see how a non-const argument, would not work - problem/error free - in the OP's code because a temporary is still a valid object in that it *is* useable as an rvalue and lvalue *until* the end of the semicolon of the statement where vector::cross is called.

    No. an rvalue cannot be both an rvalue and an lvalue. Technically neither an lvalue can. But what distinguishes them is that lvalues can be used either on the right or left hand-side of an assignment, while an rvalue cannot. It can only be used on the right-hand side.

    What happens when vector::cross is called?

    First, there's all the name matching thing going on. We can skip that. We are interested on the building of that 2nd parameter....

    That second argument (p - body.x) is evaluated. A matching operator overload is found and the code within is ran. It then returns a Vector object as an rvalue. Just an rvalue. It can't be used as the left-hand side of an assignment. You are about to know why I'm giving so much importance to this.

    The function declaration defined the second parameter as a plain reference to Vector. As you know references refer directly to the objects they "point" to. They are simply just another name for that object. But what object is this? Certainly not the return value from the operator overload. If it was, it had to be an lvalue.

    Why? Well look closely at the following 3 lines:

    Code:
    int foo = 12;
    int &bar = foo;
    bar = 23;
    bar is a reference to foo. When I change bar's value I am actually changing foo's value, as you well know. But for foo value to be changed, it has to be on the left-hand side of an assignment. So, foo has to be a lvalue.

    Since the return statement from the subtraction is an rvalue, it cannot be directly made a reference of. Because it assumes the value of the reference can be changed. But it can only change if the object it refers too is an lvalue as you saw from the code above.

    Const references (or references to const. More correct) change all that. Since the value cannot be changed, it is safe to build the reference from an rvalue. What will happen is that a temporary will first be created. This temporary will be assigned the value of the operator overload. Then the Vector reference parameter will be created and assigned with the temporary.

    > The reason, as I have come to know, why temporary rvalue objects should be treated as an rvalue only and marked as const, is because they only exist for a short period and once the end of the statement (or sequence point) is reached where the temporary object was created, then, the object will be destroyed. So I don't see the OP's code, without the constness argument, problematic.

    I hope the above clarified it. The temporary exists after the return object has been created. This will be a real-live local object. The temporary is created from it and the parameter will be created from the temporary.
    Last edited by Mario F.; 10-01-2006 at 01:58 PM.
    Originally Posted by brewbuck:
    Reimplementing a large system in another language to get a 25% performance boost is nonsense. It would be cheaper to just get a computer which is 25% faster.

  14. #14
    Registered User
    Join Date
    Mar 2005
    Posts
    135
    Quote Originally Posted by Mario F.
    First, just for clarification, there is a third reason for the parameter to be made const. Non const parameters cannot be used for const arguments. So we again needlessly restrict the use of the function. As a rule of thumb, the keyword const should be used proactively all over our code.

    But to answer your question, I'm sure others will be able to give you a better explanation. But this is my try:


    > operator -, returns by value as we all know, but, after the return statement, a unnamed temporary is created W/the value of the return statment and this results in an rvalue.

    Correct.

    > I don't see how a non-const argument, would not work - problem/error free - in the OP's code because a temporary is still a valid object in that it *is* useable as an rvalue and lvalue *until* the end of the semicolon of the statement where vector::cross is called.

    No. an rvalue cannot be both an rvalue and an lvalue. Technically neither an lvalue can. But what distinguishes them is that lvalues can be used either on the right or left hand-side of an assignment, while an rvalue cannot. It can only be used on the right-hand side.

    What happens when vector::cross is called?

    First, there's all the name matching thing going on. We can skip that. We are interested on the building of that 2nd parameter....

    That second argument (p - body.x) is evaluated. A matching operator overload is found and the code within is ran. It then returns a Vector object as an rvalue. Just an rvalue. It can't be used as the left-hand side of an assignment. You are about to know why I'm giving so much importance to this.

    The function declaration defined the second parameter as a plain reference to Vector. As you know references refer directly to the objects they "point" to. They are simply just another name for that object. But what object is this? Certainly not the return value from the operator overload. If it was, it had to be an lvalue.

    Why? Well look closely at the following 3 lines:

    Code:
    int foo = 12;
    int &bar = foo;
    bar = 23;
    bar is a reference to foo. When I change bar's value I am actually changing foo's value, as you well know. But for foo value to be changed, it has to be on the left-hand side of an assignment. So, foo has to be a lvalue.

    Since the return statement from the subtraction is an rvalue, it cannot be directly made a reference of. Because it assumes the value of the reference can be changed. But it can only change if the object it refers too is an lvalue as you saw from the code above.


    Const references (or references to const. More correct) change all that. Since the value cannot be changed, it is safe to build the reference from an rvalue. What will happen is that a temporary will first be created. This temporary will be assigned the value of the operator overload. Then the Vector reference parameter will be created and assigned with the temporary.


    > The reason, as I have come to know, why temporary rvalue objects should be treated as an rvalue only and marked as const, is because they only exist for a short period and once the end of the statement (or sequence point) is reached where the temporary object was created, then, the object will be destroyed. So I don't see the OP's code, without the constness argument, problematic.

    I hope the above clarified it. The temporary exists after the return object has been created. This will be a real-live local object. The temporary is created from it and the parameter will be created from the temporary.
    First, I went off on vacation and totally forgot to reply to this post before leaving. So sorry.

    From the boldfaced quote above, you're telling me that a reference can be bound directly to the unnamed temporary rvalue, or, that *another* temporary can be created, aside from the one already created from the returned subtraction operator, to bound to the reference?

    I didn't understand rvalues and lvalues very well until I read your post, Mario. Another question I have is why is a temporary not allowed to also be an lvalue? It's an object isn't it? or is it as I'm suspectiing: this temporary object isn't associated with an address to be able to store what's assigned to it because it's like adding two integers (1 + 2) and then expecting to store a value to the result of the addition when it's not possible to do so because the result (temporary) of the addition is not bound to an address like named-objects are - so it's like trying to assign something to something that has a value but is in thin air, if you know what I mean...

    Bottom line: all temporaries are rvalues, right?

    Thanks
    Last edited by xeddiex; 10-21-2006 at 12:04 AM.

  15. #15
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318

    Cool

    Here's a much better version of that class (incomplete same as your original post)
    Code:
    class Vector
    {
    	public:
    		Vector(float x, float y, float z) : x(x), y(y), z(z) {}
    		friend const Vector operator- (const Vector &l, const Vector &r)
    		{
    			return Vector(l.x - r.x, l.y - r.y, l.z - r.z);
    		};
    		
    		friend const Vector cross(const Vector &u, const Vector &v)
    		{
    			return Vector(u.y * v.z - u.z * v.y, u.z * v.x - u.x * v.z, u.x * v.y - u.y * v.x);
    		};
    		
    		float x, y, z;
    };
    Take note of all of the extra consts. They are ALL useful for preventing things like "if (a - b = c)" which wont give an error or warning without the return value being const. Also make use of constructors etc as shown.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Getting an error with OpenGL: collect2: ld returned 1 exit status
    By Lorgon Jortle in forum C++ Programming
    Replies: 6
    Last Post: 05-08-2009, 08:18 PM
  2. Initialized reference in function
    By DrSnuggles in forum C++ Programming
    Replies: 22
    Last Post: 07-24-2008, 04:06 PM
  3. In over my head
    By Shelnutt2 in forum C Programming
    Replies: 1
    Last Post: 07-08-2008, 06:54 PM
  4. Troubleshooting Input Function
    By SiliconHobo in forum C Programming
    Replies: 14
    Last Post: 12-05-2007, 07:18 AM
  5. Including lib in a lib
    By bibiteinfo in forum C++ Programming
    Replies: 0
    Last Post: 02-07-2006, 02:28 PM