when we overloading a function: what matters is only the argument list(inclued the types) or the return type too?
i'm overloading the assigment operator, but seems that i can't use with diferent return types:(
please someone explain to me

Recommended Answers

All 10 Replies

you need to have the return type specified as well

it depends on what you're overloading, if its operators then the return type must match to what the data is being assigned to or the operator type. with normal functions then the return type can also be different as long as the paremeters are also different (i think).

assuming you're overloading the assignment operator in a class, the retun type should be a reference to the itself, this is because if you're using multiple operators in one statement the data needs to be passed along to the next operator for that operator to use otherwise such a statement wouldn't work.

Overloading (of function or operators) is done only with the parameters, not the return type. If you have two function with the same name (or operator), same parameters and a different return type, the compiler is going to say that the call is ambiguous (it cannot choose between the two versions). The way that the compiler decides which overload to call is called "Argument-Dependent Lookup" (ADL) or colloquially as "Koenig Lookup" (after the Andrew Koenig who came up with those rules, long ago). As the name implies, the choice of overload is dependent on the arguments (parameters) only.

As far as the rules go between operators and functions, there is absolutely no difference whatsoever. As far as the language rules go, operator overloading is nothing more than syntax sugar (it's nicer to be able to write a + b than add(a,b), but for the compiler, it is only a different way to name a function and call it (with some restriction on the number of parameters)). The overloading rules are identical.

The assignment operator is a bit special because the compiler will use any assignment operator that can do a "copy" (or "move") to replace the compiler-generated versions of it, and it can only be defined as a member function (as opposed to most other operators that are allowed to be defined as free functions (and usually are, for good coding practice)). But, that's it. There is no requirement that the assignment operator should return a reference to the "this" object, however, it's conventional practice to do so in order to be conformant with the semantics of built-in assignment operators (e.g., where you can do c = a = b; to assign both a and c with the value of b).

But yeah, in summary, you cannot overload functions based on the return type.

The reason for this is a bit complicated to explain. It has to do with the complexity of trying to figure out which return-type is better at the call-site. Basically, you would end up chaining all of the argument-dependent lookups together. For example, if you have an expression like bar( foo( a + b ) ), then, the compiler would have to consider every possible overload of the + operator, and before choosing which one to use, it would have to look at their return types and see which return type fits better into the overloads of the foo function, which will involve checking the possible overloaded return types of foo, and then seeing which one works best into the bar call, and so on. This can become very difficult very quickly (it's a combinatorial explosion), especially when you start considering implicit conversions and other conversions, and then you will have template argument deduction coming into the mix. This is simply not feasible, within any bounds of reason, i.e., it does not scale beyond trivial examples with only a few calls with only a few overloads. The rules would get too complicated and require too much effort by the compiler. By excluding return types from the decision, the decision on which overloaded function to call is limited to a single call, i.e., determining which function has the best-fitting parameters, which determines the return type, which is then used for subsequent overloaded calls (if any). This is the only practical way to do this, every call is resolved in isolation by considering only its parameter.

That said, you are, of course, allowed to have different return types in different overloaded functions, but those functions will have to have either a different name or a different set of parameters, to remove the ambiguity.

N.B.: There are many ways to work around this restriction, including SFINAE (Substitution Failure Is Not An Error). If you tell us what your specific problem is, I'm sure we can find a solution that avoid having to overload based on the return-type.

see these 2 functions:

template <typename B>
    B operator =(Variant &value)
    {
        return atof(value.result.c_str());
    }

    string operator =(Variant &value)
    {
        return value.result;
    }

imagine that i need, too, return a char*.... if i do a 3rd function, the compiler give me an error :(

That does not make any sense. The assignment operator has to be a member function of the class that is on the left-hand-side of the = sign. Please provide the class declaration in which these operators are defined.

#include <iostream>
#include <string.h>
#include <typeinfo.h>

using namespace std;

class Variant
{
private:
    string result;
public:

    Variant()
    {
    }

    template<typename B>
    Variant(B val)
    {
        result=to_string(val);
    }

    Variant(string val)
    {
        result=val;
    }

    Variant(const char val)
    {
        result=val;
    }

    Variant(const char *val) //i try these, but seems ignored :(
    {
        result=string(val);
    }

    Variant(bool val)
    {
        result=to_string(val);
        if (result=="0")
        {
            result="false";
        }
        else
        {
            result="true";
        }
    }

    friend ostream& operator<< (ostream &out, Variant &VResult)
    {
       out <<VResult.result;
       return out;
    }

    friend istream& operator >> (istream &in, Variant &VResult)
    {
        getline(in,VResult.result);
        return in;
    }

    operator int() const
    {
        return atoi(result.c_str());
    }

    operator long() const
    {
        return atol(result.c_str());
    }

    operator double() const
    {
        return atof(result.c_str());
    }

    operator string() const
    {
        return result.c_str();
    }

    template <typename B>
    B operator =(Variant &value)
    {
        return atof(value.result.c_str());
    }

    string operator =(Variant &value)
    {
        return value.result;
    }
};

int main()
{
    char *test="hjello";
    Variant a=test[2];//yah... i must fix these too ;)
    cout << a;
    return 0;
}

for now, forget that comments.... if is needed, i do another topic ;)

In this case, you don't need the assignment operator at all. The default (compiler-generated) version is going to work just fine. This is because your only data member in the Variant class is the string result, which is correctly copied in the default assignment operator.

sorry why i get an error with these exemple:

#include <iostream>
#include <string.h>
#include <typeinfo.h>

using namespace std;

class Variant
{
private:
    string result;
public:

    Variant()
    {
    }

    template<typename B>
    Variant(B val)
    {
        result=to_string(val);
    }

    Variant(string val)
    {
        result=val;
    }

    Variant(const char val)
    {
        result=val;
    }

    Variant(const char *val)
    {
        result=string(val);
    }

    Variant(bool val)
    {
        result=to_string(val);
        if (result=="0")
        {
            result="false";
        }
        else
        {
            result="true";
        }
    }

    friend ostream& operator<< (ostream &out, Variant &VResult)
    {
       out <<VResult.result;
       return out;
    }

    friend istream& operator >> (istream &in, Variant &VResult)
    {
        getline(in,VResult.result);
        return in;
    }

    operator int() const
    {
        return atoi(result.c_str());
    }

    operator long() const
    {
        return atol(result.c_str());
    }

    operator double() const
    {
        return atof(result.c_str());
    }

    operator string() const
    {
        return result.c_str();
    }
};

int main()
{
    string b;
    Variant a;
    b="oi";
    a=b;
    a="oi";
    b=a;// error
    cout << b;
    return 0;
}

but not with numbers :(
"error: ambiguous overload for 'operator=' (operand types are 'std::string {aka std::basic_string<char>}' and 'Variant')"

The problem is that almost anything can convert to char, including all your implicit conversion types int, long and double. Once you eliminate all those conversion operators, it works:

#include <iostream>
#include <string>

using namespace std;

class Variant
{
private:
    string result;
public:

    Variant()
    {
    }

    template<typename B>
    Variant(B val)
    {
        result=to_string(val);
    }

    Variant(string val)
    {
        result=val;
    }

    Variant(const char val)
    {
        result=val;
    }

    Variant(const char *val)
    {
        result=string(val);
    }

    Variant(bool val)
    {
        result=to_string(val);
        if (result=="0")
        {
            result="false";
        }
        else
        {
            result="true";
        }
    }

    friend ostream& operator<< (ostream &out, Variant &VResult)
    {
       out <<VResult.result;
       return out;
    }

    friend istream& operator >> (istream &in, Variant &VResult)
    {
        getline(in,VResult.result);
        return in;
    }

    operator const string&() const
    {
        return result;
    }

    operator string&() 
    {
        return result;
    }
};

int main()
{
    string b;
    Variant a;
    b="oi";
    a=b;
    a="oi";
    b=a;// OK
    cout << b;
    return 0;
}

This is why these types of variant classes (like boost::variant or boost::any) never have implicit conversion operators, they always rely on explicit conversions (or cast functions). This works:

#include <iostream>
#include <string>

using namespace std;

class Variant
{
private:
    string result;
public:

    Variant()
    {
    }

    template<typename B>
    Variant(B val)
    {
        result=to_string(val);
    }

    Variant(string val)
    {
        result=val;
    }

    Variant(const char val)
    {
        result=val;
    }

    Variant(const char *val)
    {
        result=string(val);
    }

    Variant(bool val)
    {
        result=to_string(val);
        if (result=="0")
        {
            result="false";
        }
        else
        {
            result="true";
        }
    }

    friend ostream& operator<< (ostream &out, Variant &VResult)
    {
       out <<VResult.result;
       return out;
    }

    friend istream& operator >> (istream &in, Variant &VResult)
    {
        getline(in,VResult.result);
        return in;
    }

    explicit operator int() const
    {
        return atoi(result.c_str());
    }

    explicit operator long() const
    {
        return atol(result.c_str());
    }

    explicit operator double() const
    {
        return atof(result.c_str());
    }

    operator const string&() const
    {
        return result;
    }

    operator string&() 
    {
        return result;
    }
};

int main()
{
    string b;
    Variant a;
    b="oi";
    a=b;
    a="oi";
    b=a;// OK
    cout << b;
    return 0;
}

But then, any conversion to int / long / double has to be made explicitly, with something like int(a). Trying to allow implicit conversions all over the place is a very tricky task because of all these weird little issues, that's why most production-quality code avoids them completely.

thanks for that. now tell me why the char* isn't working?

int main()
{
    char *c="hello";
    Variant a=c;
    cout << a;
    return 0;
}

tell me:
"error: call of overloaded 'to_string(char&)' is ambiguous"
i understand that means that i can't convert char
to string... like saying it's the same type... so please correct me

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.