Solved

Overloading << in BCB4.0

Posted on 2000-03-03
10
429 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
  • 6
  • 4
10 Comments
 
LVL 22

Accepted Solution

by:
nietod earned 50 total points
Comment Utility
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
Comment Utility
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
Comment Utility
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
 

Author Comment

by:shadow66
Comment Utility
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
Comment Utility
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
Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

 
LVL 22

Expert Comment

by:nietod
Comment Utility
>> 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
Comment Utility
>> 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
Comment Utility
>>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
Comment Utility
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
Comment Utility
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

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
Go is an acronym of golang, is a programming language developed Google in 2007. Go is a new language that is mostly in the C family, with significant input from Pascal/Modula/Oberon family. Hence Go arisen as low-level language with fast compilation…
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.

743 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

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now