views:

130

answers:

4

Guys having code :

#include "stdafx.h"
#include "Record.h"

template<class T>//If I make instead of template regular fnc this compiles  
//otherwise I'm getting an error (listed on the very bottom) saying  
// that operator << is ambiguous, WHY?
ostream& operator<<(ostream& out, const T& obj)
{
    out << "Price: " 
        << (obj.getPrice()) << '\t'//this line causes this error
        << "Count: "
        << obj.getCount()
        << '\n';
    return out;
}

int _tmain(int argc, _TCHAR* argv[])
{
    vector<Record> v;
    v.reserve(10);
    for (int i = 0; i < 10; ++i)
    {
        v.push_back(Record(rand()%(10 - 0)));
    }
    copy(v.begin(),v.end(),ostream_iterator<Record>(cout, "\n"));
    return 0;
}

//Record class
class Record
{
    private:
        int myPrice_;
        int myCount_;
        static int TOTAL_;
    public:
        Record(){}
        Record(int price):myPrice_(price),myCount_(++TOTAL_)
        {/*Empty*/}
        int getPrice()const
        {
            return myPrice_;
        }

        int getCount()const
        {
            return myCount_;
        }
        bool operator<(const Record& right)
        {
            return (myPrice_ < right.myPrice_) && (myCount_ < right.myCount_);
        }
};

int Record::TOTAL_ = 0;

Error 2 error C2593: 'operator <<' is ambiguous

A: 

If you specify your template with the generic class T, and you pass a simple reference, you can't expect that obj.getPrice() will be OK. You should make a parent class for Record that has the getPrice function, and build the template around that class. That way you will have no problems with defining the operator. But the way you are doing it is wrong, you can't expect any possible class to have the .getPrice() function.. and that's why the compiler warns you about this.

John Paul
@John Paul I have to think about it but for the moment I'm not really convinced that what you are saying is true; Why can't I expect every class for this template to have getPrice() method?
There is nothing we can do
@John You can make a template with whatever code you want as long as it's syntactically valid. The only time the operations you perform on a parameterized type is checked is when you actually USE those operations. http://en.wikipedia.org/wiki/Substitution_failure_is_not_an_error
Cogwheel - Matthew Orlando
No, I'm pretty sure John is right. While `Record` may have the method, the template doesn't know that.
Brian S
@John Paul: You don't need a common base class for generic (template) programming. Indeed, by limiting yourself this way, you're taking away one of the strongest features of C++. Sorry, but -1.
DevSolar
@Brian S: read the link at the end of my comment.
Cogwheel - Matthew Orlando
John Paul
+7  A: 

The concept behind operator<<( ostream &, ... ) is that every class can have its own overload, handling that specific class in a way that make sense.

That means you get operator<<( ostream &, const Record & ) which handles Record objects, and operator<<( ostream &, const std::string & ) which handles standard strings, and operator<<( ostream &, const FooClass & ) which handles FooClass objects. Each of these functions knows how to handle the object type it has been declared for, because each of them requires a different handling. (E.g. getPrice() / getCount() for Record, or getFoo() / getBar() for FooClass.)

Your template is trampling roughshod over the whole concept. By defining it as a template function (which would match any class), you not only collide with the many definitions of operator<<() already in the standard / your codebase, but all possible overloadings.

How could the compiler decide whether to use operator<<( ostream &, const std::string & ) or your template? It cannot, so it throws up its hands in despair and gives up. That's what the error is telling you.

DevSolar
There is nothing we can do
Well, Jerry's answer (which you accepted) doesn't say anything different: "The problem is arising because the compiler doesn't know whether to use the normal overload to print out the string, or to use the template being defined to print it out." That is what "ambiguos" means: Using the rules of the language, the compiler ends up with more than one `operator<<()` that would fit, and cannot decide which to use. Make no mistake, this is a *programming error*, not some kind of stupidity on behalf of the compiler. It cannot read minds; your code must be unambiguous.
DevSolar
+3  A: 

First, you need to read the error message more carefully. As an alternative, consider breaking the statement up, something like this:

out << "Price: ";
out << (obj.getPrice());
out << "\tCount: ";
out << obj.getCount();
out << '\n';

When you do, you'll realize that what's really causing the problem is not where you try to print out getPrice(), but where you try to print out "Price: ".

The problem is arising because the compiler doesn't know whether to use the normal overload to print out the string, or to use the template being defined to print it out. The latter would cause infinite recursion, and it couldn't actually compile since it requires an object on which you can/could call getPrice and getCount to compile correctly -- but it has a signature that matches, so the compiler says it's ambiguous, and that's the end of that.

Jerry Coffin
@Jerry thank you for this explanation. So I'm guessing line number provided by compiler is wrong?
There is nothing we can do
@Jerry but what puzzels me why compiler didn't pick specialized version for string and int?
There is nothing we can do
@A-ha: It's pretty typical for the compiler to list a line or two after the location of the real problem (usually when it gets to something that makes it clear that what was already processed had a problem). It can't pick one over the other because neither requires a conversion, so they're equally good matches.
Jerry Coffin
A: 

The reason of the error is that your templated overload is conflicting with another templated overload, and there is no reason to prefer one template to another:

template<class charT, class traits>
basic_ostream<charT,traits>& operator<<(basic_ostream<charT,traits>&, const charT*);

template <class T>
basic_ostream<char, char_traits<char> >& operator<< (basic_ostream<char, char_traits<char> >&, const T&);

//which one is preferable when you ask for: cout << "literal";?

(ostream should be a typedef for basic_ostream<char, char_traits<char> >.)

The whole idea of making your overload a template is questionable, seeing that the overload clearly cannot handle any other class than your Record.

There probably are techniques to allow you to provide a single templated overload for a number of unrelated Record-like classes with a little template metaprogramming (enable_if and traits), if that is the idea.

UncleBens