Motti
Motti

Reputation: 114715

Overload a C++ function according to the return value

We all know that you can overload a function according to the parameters:

int mul(int i, int j) { return i*j; }
std::string mul(char c, int n) { return std::string(n, c); } 

Can you overload a function according to the return value? Define a function that returns different things according to how the return value is used:

int n = mul(6, 3); // n = 18
std::string s = mul(6, 3); // s = "666"
// Note that both invocations take the exact same parameters (same types)

You can assume the first parameter is between 0-9, no need to verify the input or have any error handling.

Upvotes: 43

Views: 26890

Answers (16)

paercebal
paercebal

Reputation: 83309

You have to tell the compiler which version to use. In C++, you can do it three ways.

Explicitly differentiate the calls by typing

You somewhat cheated because you sent an integer to a function waiting for a char, and wrongly sent the number six when the char value of '6' is not 6 but 54 (in ASCII):

std::string mul(char c, int n) { return std::string(n, c); }

std::string s = mul(6, 3); // s = "666"

The right solution would be, of course,

std::string s = mul(static_cast<char>(54), 3); // s = "666"

This was worth mentioning, I guess, even if you did not want the solution.

Explicitly differentiate the calls by dummy pointer

You can add a dummy parameter to each functions, thus forcing the compiler to choose the right functions. The easiest way is to send a NULL dummy pointer of the type desired for the return:

int mul(int *, int i, int j) { return i*j; }
std::string mul(std::string *, char c, int n) { return std::string(n, c); }

Which can be used with the code:

int n = mul((int *) NULL, 6, 3); // n = 18
std::string s = mul((std::string *) NULL, 54, 3); // s = "666"

Explicitly differentiate the calls by templating the return value

With this solution, we create a "dummy" function with code that won't compile if instantiated:

template<typename T>
T mul(int i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

You'll note this function won't compile, which is a good thing because we want only to use some limited functions through template specialization:

template<>
int mul<int>(int i, int j)
{
   return i * j ;
}

template<>
std::string mul<std::string>(int i, int j)
{
   return std::string(j, static_cast<char>(i)) ;
}

Thus, the following code will compile:

int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>(54, 3); // s = "666"

But this one won't:

short n2 = mul<short>(6, 3); // error: assignment of read-only variable ‘k’

Explicitly differentiate the calls by templating the return value, 2

Hey, you cheated, too!

Right, I did use the same parameters for the two "overloaded" functions. But you did start the cheating (see above)...

^_^

More seriously, if you need to have different parameters, then you will to write more code, and then have to explicitly use the right types when calling the functions to avoid ambiguities:

// For "int, int" calls
template<typename T>
T mul(int i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

template<>
int mul<int>(int i, int j)
{
   return i * j ;
}

// For "char, int" calls
template<typename T>
T mul(char i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

template<>
std::string mul<std::string>(char i, int j)
{
   return std::string(j, (char) i) ;
}

And this code would be used as such:

int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>('6', 3); // s = "666"

And the following line:

short n2 = mul<short>(6, 3); // n = 18

Would still not compile.

Conclusion

I love C++...

:-p

Upvotes: 57

John Littleberry Sr
John Littleberry Sr

Reputation: 21

Short and simple, the answer is NO. In C++ the requirements are:

1: name of functions MUST be the same
2: set of arguments MUST differ
*The return type can be the same or different

//This is not valid
    int foo();
    float foo();

    typedef int Int;

    int foo(int j);
    int foo(Int j);

//Valid:
   int foo(int j);
   char* foo(char * s);
   int foo(int j, int k);
   float foo(int j, float k);
   float foo(float j, float k);

Upvotes: 2

justin romaine
justin romaine

Reputation: 11

OK you geniuses ;) this is how you do it like a pro.


class mul
{
 int m_i,m_j;
public:
 mull(int i,int j):m_i(i),m_j(j){}
 template
 operator R() 
 {
  return (R)m_i * m_j;
 }
};

use like


double d = mul(1,2);
long l = mul(1,2);

no stupid <>

Upvotes: -1

Eclipse
Eclipse

Reputation: 45493

If you wanted to make mul be a real function instead of a class, you could just use an intermediate class:

class StringOrInt
{
public:
    StringOrInt(int p1, int p2)
    {
        param1 = p1;
        param2 = p2;
    }
    operator int ()
    {
        return param1 * param2;
    }

    operator std::string ()
    {
        return std::string(param2, param1 + '0');
    }

private:
    int param1;
    int param2;
};

StringOrInt mul(int p1, int p2)
{
    return StringOrInt(p1, p2);
}

This lets you do things like passing mul as a function into std algorithms:

int main(int argc, char* argv[])
{
    vector<int> x;
    x.push_back(3);
    x.push_back(4);
    x.push_back(5);
    x.push_back(6);

    vector<int> intDest(x.size());
    transform(x.begin(), x.end(), intDest.begin(), bind1st(ptr_fun(&mul), 5));
    // print 15 20 25 30
    for (vector<int>::const_iterator i = intDest.begin(); i != intDest.end(); ++i)
        cout << *i << " ";
    cout << endl;

    vector<string> stringDest(x.size());
    transform(x.begin(), x.end(), stringDest.begin(), bind1st(ptr_fun(&mul), 5));
    // print 555 5555 55555 555555
    for (vector<string>::const_iterator i = stringDest.begin(); i != stringDest.end(); ++i)
        cout << *i << " ";
    cout << endl;

    return 0;
}

Upvotes: 23

Coincoin
Coincoin

Reputation: 28596

class mul
{
public:
    mul(int p1, int p2)
    {
        param1 = p1;
        param2 = p2;
    }
    operator int ()
    {
        return param1 * param2;
    }

    operator std::string ()
    {
        return std::string(param2, param1 + '0');
    }

private:
    int param1;
    int param2;
};

Not that I would use that.

Upvotes: 47

mempko
mempko

Reputation: 316

You can use the functor solution above. C++ does not support this for functions except for const. You can overload based on const.

Upvotes: 0

Federico A. Ramponi
Federico A. Ramponi

Reputation: 47075

Let mul be a class, mul(x, y) its constructor, and overload some casting operators.

Upvotes: 4

Brian
Brian

Reputation: 118865

I presume you could have it return some weird type Foo that just captures the parameters and then Foo has an implicit operator int and operator string, and it would "work", though it wouldn't really be overloading, rather an implicit conversion trick.

Upvotes: 2

Pieter
Pieter

Reputation: 17705

Use implicit conversion in an in between class.

class BadIdea
{
  public:
    operator string() { return "silly"; }
    operator int() { return 15; }
};

BadIdea mul(int, int)

You get the idea, terrible idea though.

Upvotes: 8

Paolo Tedesco
Paolo Tedesco

Reputation: 57202

You could do something like

template<typename T>
T mul(int i,int j){
    return i * j;
}

template<>
std::string mul(int i,int j){
    return std::string(j,i);
}

And then call it like this:

int x = mul<int>(2,3);
std::string s = mul<std::string>(2,3);

There is no way of overloading on the return value.

Upvotes: -1

warren
warren

Reputation: 33445

Not in C++. What you'd get in the above example would be the returned value which is an int cast into something string can understand, most likely a char. Which would be ASCII 18 or "device control 2".

Upvotes: 0

Franci Penov
Franci Penov

Reputation: 76001

You cannot overload a function based on the return value only.

However, while strictly speaking this is not an overloaded function, you could return from your function as a result an instance of a class that overloads the conversion operators.

Upvotes: 3

AshtonKJ
AshtonKJ

Reputation: 1386

Put it in a different namespace? That would be how I would do it. Not strictly an overload, rather a just having two methods with the same name, but a different scope (hence the :: scope resolution operator).

So stringnamespace::mul and intnamespace::mul. Maybe its not really what you are asking, but it seems like the only way to do it.

Upvotes: -1

Joel Coehoorn
Joel Coehoorn

Reputation: 415765

You could use a template, but then you'd have to specify the template parameter when you make the call.

Upvotes: -1

Zebra North
Zebra North

Reputation: 11492

No.

You can't overload by return value because the caller can do anything (or nothing) with it. Consider:

mul(1, 2);

The return value is just thrown away, so there's no way it could choose an overload based on return value alone.

Upvotes: 21

xtofl
xtofl

Reputation: 41509

As far as I know, you can't (big pity, though...). As a workaround, you can define an 'out' parameter instead, and overload that one.

Upvotes: 0

Related Questions