• Advertisement
Sign in to follow this  

constructing an object and passing it by reference

This topic is 4427 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Can someone help me with the following?
class foo
{
  public:
    foo(int i) { in = i; }
  int in;

  inline bool operator< (foo &ref)
  {
    return (in < ref.in);
  }
};

void func()
{
  foo a(10);

  if (a < foo(5)) // this won't work cuz operator< expects a reference
  // is there a way around this?
  ...

}

Share this post


Link to post
Share on other sites
Advertisement
Just a note: might want that function to take a const Foo & and not just a Foo &. Moreover the function itself should be const, as it isn't modifying the lhs of the equation.

Share this post


Link to post
Share on other sites
Oh! And there is a choice you should make. Do you want implicit conversion from an int to a Foo? If so, you don't need to a < Foo( 5 ) and can just a < 5.

If you don't want implicit conversion (which I definatly suggest) then you should make that constructor explicit.

Share this post


Link to post
Share on other sites
Quote:
Original post by bradbobak

if (a < foo(5)) // this won't work cuz operator< expects a reference
// is there a way around this?

Yes. The parameter must be a const reference for passing temporary objects to work. You should always pass arguments as either a const reference or a value, if you aren't going to modify the argument. Also, the function should itself be marked const to indicate that *this is not going to be modified.

bool operator < (const foo &ref) const

Moreover, the operator < should probably be a free function, that is:

// outside a class
bool operator < (const foo &left, const foo &right);


If you need to access foo's private members, declare the function as a friend; in this case, you can also define it right there in the friend declaration, like this:

class foo
{
private:
int in;
public:
friend bool operator < (const foo &left, const foo &right)
{
return left.in < right.in;
}
};

You don't need to use the inline keyword when defining a function inside a class declaration.

Also, you should make a habit of using initializer lists in constructors:

foo(int i) : in(i) { }


EDIT: ProgramMax: Guilty as charged :/

Share this post


Link to post
Share on other sites
I'd rate you both up, except I already have. [rolleyes]

Share this post


Link to post
Share on other sites
I'm sorry.. I rated you now..

Another question -->


class numeric
{
private:
int val;

public:
numeric(const int i) { val = i; }

operator int() const
{
return (val);
}
};

void foo()
{
numeric n1(10);

if (n1 == 10)
do_something();
if (10 == n1)
do_something_else();
}


I can see this working in the 'if (nl == 10}' part but how do I get it to work in the 'if (nl == 10)' part.. won't it try to convert '10' to a numeric??


Share this post


Link to post
Share on other sites
or.. do I declare both operators outside the class..

bool operator == (const numeric &n1, int i) ...
bool operator == (int i, const numeric &n1) ...

??

EDIT: and can I call one from the other.. like

bool operator == (int i, const numeric &n1)
{
return (operator == (n1, i));
}

Share this post


Link to post
Share on other sites
For a lot more on const correctness check out the C++ Lite FAQ. It's just plain awesome.

http://www.parashift.com/c++-faq-lite/const-correctness.html

Share this post


Link to post
Share on other sites
Yes, if you want type conversions done on both parameters then you should make your operators free functions, and your constructor non-explicit. (you'll only need one function per operator then, that takes two numerics)

EDIT: it's preferred to be able to treat custom types - especially those resembling numbers - as close to built-in types as possible. That includes creating free function operators instead of member function operators, and returning a const value from arithmetic operators.

Share this post


Link to post
Share on other sites
Quote:
Original post by stylin
(you'll only need one function per operator then, that takes two numerics)


Unfortunately, then you'll have an ambiguity. The compiler'll find two equal candidates in overload resolution, namely:

operator < (int, int)

and

operator < (numeric const&, numeric const&)

This is one of the reasons that implicit conversions should be used judiciously. If you have both an implicit converting constructor and an overloaded conversion operator, problems are bound to arise.

Share this post


Link to post
Share on other sites
The compiler easily resolves that ambiguity, preferring the built-in ops - at least mine does (VC++8).

edit: and in this case - operator< - it wouldn't really make sense to compare two ints when you want to compare two numerics. So it would probably be good to explicitly state your intentions, especially for those reading the code.

if( numeric( someInt ) < numeric( anotherInt ) )
std::cout << "made it clear we are comparing numerics" << std::endl;


[Edited by - stylin on January 5, 2006 2:36:30 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by stylin
The compiler easily resolves that ambiguity, preferring the built-in ops - at least mine does (VC++8).


The question is not 'will the compiler resolve the ambiguity?', but rather 'will the compiler understand what the programmer is thinking of?'. In that regard, the best way to resolve an ambiguity (for the compiler) is to output an error and ask for more information, instead of expecting the programmer to know what he is doing (which he doesn't).

Share this post


Link to post
Share on other sites
Quote:
Original post by ToohrVyk
The question is not 'will the compiler resolve the ambiguity?', but rather 'will the compiler understand what the programmer is thinking of?'.

If I see a line of code that ops two ints, I'm going to assume the programmer was thinking of comparing two ints - what else would I assume?

Quote:
In that regard, the best way to resolve an ambiguity (for the compiler) is to output an error and ask for more information, instead of expecting the programmer to know what he is doing (which he doesn't).

I don't consider defaulting to built-in ops for built-in types erroneous. It's the most intuitive solution I can think of. See my edit above.

Share this post


Link to post
Share on other sites
Hmm... I don't think it should; indeed, at least all gcc versions I've used have complained about it:
Quote:

foo.cpp:14: error: ambiguous overload for 'operator<' in 'f < 1'
foo.cpp:14: error: candidates are: operator<(int, int) <built-in>
foo.cpp:9: error: bool operator<(const foo&, const foo&)


The overload resolution rules in the Standard are quite complex, but I'm pretty sure that nowhere is it said that a userdef->builtin conversion is "better" than the other way around. Here's everything you have ever wanted to know about overload matching, and probably a lot more.

Share this post


Link to post
Share on other sites
If you are using two ints, there wouldn't be an ambiguity (since the basic op requires no cast, but the numeric op requires two). However, if you have both a numeric and an int, both the basic and numeric op require one cast, and create an ambiguity.

And, depending on the situation, I find myself wanting one or the other: transform an integer into a time format for equality comparison (instead of the reverse), and transform a fixed-points base 10 currency into an integer (instead of the reverse).

Share this post


Link to post
Share on other sites
Ho-humm. I should have elaborated that I was speaking about the cases the original poster asked about, namely numeric OP int and int OP numeric. Those will be ambiguous if all you have is operator OP (numeric, numeric). Sorry I wasn't more clear about it. int OP int and numeric OP numeric will of course be kosher.

Anyway, in general, if there's even a slightest danger of confusion or ambiguity, you should use explicit conversions, and preferably almost everywhere else, too.

Share this post


Link to post
Share on other sites
Ok, I'll try and find out how VC++8 differs from the Standard in this regard. Here is my test code. This behaviour is wrong, then (Mixed-operands are sent to our foo operator)?

#include <iostream>

struct foo {
foo( int i ) : in( i ) {}
int in;
};

bool operator < ( const foo & lhs, const foo & rhs ) {
std::cout << "( foo < foo ) : ";
return lhs.in < rhs.in;
}

int main() {

if( foo(69) < foo(420) )
std::cout << "compared foos" << std::endl;
if( 69 < 420 )
std::cout << "compared ints" << std::endl;
if( foo(69) < 420 )
std::cout << "compared foo & int" << std::endl;
if( 69 < foo(420) )
std::cout << "compared int & foo" << std::endl;
return 0;
}



edit: Ahh, just read your last post ToohrVyk. I see what you mean by mind-reading now. It's not standard, but I've usually tried to reserved the lhs type as the dominant one, along with a nice, in-function comment.

Share this post


Link to post
Share on other sites
No, that's not ambiguous. It should compile, and each of those comparisons, except the second one, should call operator < (foo, foo).

It's when you add an foo::operator int() that the latter two will become ambiguous; both of them are then exactly one conversion apart from both operator < (int, int) and operator < (foo, foo).

Share this post


Link to post
Share on other sites
True - which is why I usually choose explicit "get" functions over conversion operators.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement