?
Solved

Overloading << in BCB4.0

Posted on 2000-03-03
10
Medium Priority
?
437 Views
Last Modified: 2012-05-04
Compiling the following console app using Borland C++ Builder 4.0 results in an error:

#include <condefs>
#include <fstream>
#pragma hdrstop

class A {
    public:
        A(int val) : value(val) {}
        int getValue() {return value;}
    private:
        int value;
};

std::ofstream& operator<<(std::ofstream& os, A& a) {
    os << a.getValue();
    return os;
}

int main()
{
    int i(1);
    A a(2);

    std::ofstream outFile("test.txt");
    outFile << i << '\n';
    outFile << a << '\n';
    outFile << int(3) << '\n';
    outFile << A(4) << '\n';              // error (see text below)
    outFile.close();

    return 0;
}

[C++ Error] experiment.cpp(27): E2094 'operator<<' not implemented in type 'std::ofstream' for arguments of type 'A'.

When I comment this line out, the program compiles, runs and writes:
1
2
3

to test.text as desired. Why can't I instantiate an A in mid-stream as I can with int?

Thanks.
0
Comment
Question by:shadow66
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 4
10 Comments
 
LVL 22

Accepted Solution

by:
nietod earned 200 total points
ID: 2581988
The problem is that the temporary A would be constant and your operator doesn't support const A.

continues.
0
 
LVL 22

Expert Comment

by:nietod
ID: 2582002
when you create an unnamed temporary like A(4) the temporary is an R value, that is, it can be used in circumstances where it is treated like a constant.  But not in circumstances where it woudl be changed.  This is because code that changes it is probably an error, because as a temporary, it is going to be quickly destroyed so the changes would be lost.   The overloaded << operator takes an A a non-constant reference.  As far as the compiler "knows", this means the operator << function will try to change the A object that is passed to it.  But this is not allowed since the A in this case is a temporary.  

continues
0
 
LVL 22

Expert Comment

by:nietod
ID: 2582012
To make this work, you can do 1 of two things.  One solution, probably not a good one is to make the overloaded << operator take the A by value instead of by reference, like

std::ofstream& operator<<(std::ofstream& os, A a)

This will work because it copies the temporary A to a local A so the compiler knows the procedure won't be able to change the temporary A.

But that is ineffiicent because the copy constructor has to be called and can be slow, especially for large or complex classes.  So a better solution is to pass the A operand by constant reference.  Like

std::ofstream& operator<<(std::ofstream& os, const A& a)

In this case a copy does not have to be made because the object is passed by reference.  But the compiler knows the procedure won't alter the temporary A object, because it is constant, so it allows the temporary A to be passed.

New if you do this is will produce another problem.  Actually expose an existing one.  The

     int getValue() {return value;}

procedure cannot be called on a cosntant A object because it is not a constant member procedure.  However there is no good reason for this.  it should be made constant, like

     int getValue() const {return value;}

let me know if you have any questions.

0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:shadow66
ID: 2582045
Nietod--thanks for posting the R value "hint" first--it gave me time to experiment. I had just completed trying what you later suggested--making getValue() constant and making the A& parameter constant:

  int getValue() const {return value;}

  fstream& operator<<(std::ofstream& os, const A& a) {
  ...
  }

Surprisingly, these two changes resulted the following error:  
[C++ Error] experiment.cpp(14): E2015 Ambiguity between 'std::basic_ostream<char,std::char_traits<char> >::operator <<(int)' and 'operator <<(std::basic_ofstream<char,std::char_traits<char> > &,const A &)'.

It's as if my overload function is too similar to some fstream member function for arguments of type int.

0
 

Author Comment

by:shadow66
ID: 2582188
Here's some additional/related behavior. This actually compiles:
#include <condefs>
#include <fstream>
#include <string>
#pragma hdrstop

class A {
    public:
        A(std::string val) : value(val){}
        std::string getValue() const {return value;}
    private:
        std::string value;
};

std::ofstream& operator<<(std::ofstream& os, const A& a) {
    os << a.getValue();
    return os;
}

int main()
{
    std::ofstream outFile("test.txt");
    outFile << A("abc") << '\n';
    outFile.close();

    return 0;
}

but when run crashes w/ an EStackOverflow exception.  

Get this: It even crashes if you rewrite main() as:

int main()
{
    std::string s("abc");

    std::ofstream outFile("test.txt");
    outFile << s << '\n';
    outFile.close();

    return 0;
}

but if you remove the class definition and overloaded function from the program, it runs fine! Why would the existence of class A and associated operator<< function affect the output of a std::string to a file?
0
 
LVL 22

Expert Comment

by:nietod
ID: 2582242
>> Surprisingly, these two changes resulted the following error:  
Which is the line that produces this.

It sounds like it needs to perform a conversion on this line and it doesn't know which conversion to use.  The two choices it finds is to convert the value to int and call the int overload (one provided for you already) or it can use your A::A(int) constructor to convert the value to an A object and call the A overload that you wrote.  The fix is usually to use a little casting to hekp it decide which way to go, but I'd like to know more about the line with the problem first.
0
 
LVL 22

Expert Comment

by:nietod
ID: 2582250
>> Why would the existence of class A and
>> associated operator<< function affect the
>> output of a std::string to a file?

I assume what is happening is that in

   outFile << s << '\n';

it can't print s directly soo it tries to convert s.  it uses the A constructor ("conversion constructor") to create an A object from s and then calls the overload you wrote for an A object.

However, I can't explain why this converison takes place.  I thought a string would output as is, whith no need to convert.  Furthermore, I can't explain the crash.  What compiler are you using?  What version?
0
 

Author Comment

by:shadow66
ID: 2582361
>>Which is the line that produces this.

Oops, ya that would be helpful. From the original program in which A's data member was of type int:

std::ofstream& operator<<(std::ofstream& os, const A& a) {
    os << a.getValue();  // This line failed (error listed below)
    return os;
}

Error: [C++ Error] experiment.cpp(15): E2015 Ambiguity between 'std::basic_ostream<char,std::char_traits<char> >::operator <<(int)' and 'operator <<(std::basic_ofstream<char,std::char_traits<char> > &,const A &)'.

You would think a.getValue() would be of type int and therefore the ofstream member function operator<<(int&) would be used. I don't get why it would even consider my operator<<(ofstream&, const A&) function for this statement, since getValue() returns an object of type int, not of type A.

>>I assume what is happening is that in
     outFile << s << '\n';
     it can't print s directly soo it tries to convert s.  

But, in the second program, if I completely remove the declaration of A and the declaration of the overloaded operator>>, then s prints just fine.

>>What compiler are you using?  What version?

Borland C++ Builder Standard, Version 4.0 Build 14.4

Thanks.
0
 
LVL 22

Expert Comment

by:nietod
ID: 2582953
Interesting.  I think this is due to a mistake in the BCB compiler or its STL library.  The code works fine in VC and it worked fine in BCB for me, until it realized one small difference in you code that i didn't have.  When I put in that change VC was fine but BCB wasn't.

std::ofstream& operator<<(std::ofstream& os, const A& a)

should work with any type if output stream, not just ofstream, so it should be declared like

std::ostream& operator<<(std::ostream& os, const A& a)

That took care of the problem.  What was happening was that you were getting conversion because inside the procedure it had

  os << a.getValue();

and to perform that it took the string returned by getValue() and converted it to an "A" object using the A(const std::string &) conversion constructor. Then to output the A object it created, it called itself recusively.

Why?  I'm not sure, it may be that the BCB STL doesn't define << for a ofstream and a std::string so it choose to convert the string to an A object to make things work rather than convert the ofstream to an ostream.  However, I believe that of the two conversions the 2nd is the right one (the standard does not leave this up the compiler, there are specific orders in which these conversions are to be performed.)
0
 

Author Comment

by:shadow66
ID: 2583102
Great! The following combination of int and string data members works exactly as I wanted:

#include <condefs>
#include <fstream>
#include <string>
#pragma hdrstop

class A {
    public:
        A(int intVal, std::string stringVal) : intValue(intVal), stringValue(stringVal) {;}
        int getIntValue() const {return intValue;}
        std::string getStringValue() const {return stringValue;}
    private:
        int intValue;
        std::string stringValue;
};

std::ostream& operator<<(std::ostream& os, const A& a) {
    os << a.getIntValue() << a.getStringValue();
    return os;
}

int main()
{

    int i(1);
    std::string s("a");
    A a(2,"b");
    std::ofstream outFile("test.txt");
    outFile << i << s << '\n';
    outFile << a << '\n';
    outFile << int(3) << std::string("c") << '\n';
    outFile << A(4,"d") << '\n';
    outFile.close();

    return 0;
}

I agree that it's odd that I couldn't use the exact  class type in my overloaded operator<< function, especially since ofstream inherts ostream. Did some digging and, sure enough, in the <ostream> header file all the member operator<< functions are declared private!

Thanks for your time and efforts. I appreciate you grabbing my question so quickly and sticking with it.
0

Featured Post

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

When writing generic code, using template meta-programming techniques, it is sometimes useful to know if a type is convertible to another type. A good example of when this might be is if you are writing diagnostic instrumentation for code to generat…
  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.
Suggested Courses

741 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question