VC choosing the wrong operator<< overload only at the first call. Bug?
-
21-09-2019 - |
Question
I spent some time removing all the uninfluent code and here is my problem.
--- File.h ---
#include <fstream>
#include <string>
template <typename Element>
class DataOutput : public std::basic_ofstream<Element>
{
public:
DataOutput(const std::string &strPath, bool bAppend, bool bBinary)
: std::basic_ofstream<Element>(
strPath.c_str(),
(bAppend ? ios_base::app : (ios_base::out | ios_base::trunc)) |
(bBinary ? ios_base::binary : 0))
{
if (is_open())
clear();
}
~DataOutput()
{
if (is_open())
close();
}
};
class File
{
public:
File(const std::string &strPath);
DataOutput<char> *CreateOutput(bool bAppend, bool bBinary);
private:
std::string m_strPath;
};
--- File.cpp ---
#include <File.h>
File::File(const std::string &strPath)
: m_strPath(strPath)
{
}
DataOutput<char> *File::CreateOutput(bool bAppend, bool bBinary)
{
return new DataOutput<char>(m_strPath, bAppend, bBinary);
}
--- main.cpp ---
#include <File.h>
void main()
{
File file("test.txt");
DataOutput<char> *output(file.CreateOutput(false, false));
*output << "test"; // Calls wrong overload
*output << "test"; // Calls right overload!!!
output->flush();
delete output;
}
And this is the output file after building with cl
and options /D "WIN32" /D "_UNICODE" /D "UNICODE"
and running
--- test.txt ---
00414114test
Basically what happens is that the first operator<<
call in main
is bound to the member method
basic_ostream<char>& basic_ostream<char>::operator<<(
const void *)
whereas the second one is (correctly) bound to
basic_ostream<char>& __cdecl operator<<(
basic_ostream<char>&,
const char *)
thus giving a different output.
This doesn't happen if i do any of the following:
- Inline
File::CreateOutput
- Change
DataOutput
with a non-template one withElement=char
- Add
*output;
before the firstoperator<<
call
Am i correct in considering this an undesired compiler behavior?
Is there any explanation for this?
Oh, and i'm using VC7 at the moment to test this simplified code but i have tried the original code in VC9 and VC8 and the same thing was happening.
Any help or even a clue is appreciated
Solution
Looks like a compiler bug. You might want to try with the latest VC compiler (which at the moment is VC10 Beta2), and if it's not fixed, follow up with the VC team (you'll need a complete self contained repo). If it is fixed, you should just use the work around you found and move on with your life.
OTHER TIPS
It is a compiler bug (not just looks like one) since it produces different call bindings for the two identical statements
*output << "test"; // Calls wrong overload
*output << "test"; // Calls right overload!!!
However, the compiler is within its rights to do this, since you have
void main()
which means that this is not a valid C++ program (void main
isn’t permitted in C either, and it has never been valid in C or C++). So, you have been running the result of compiling invalid source code. The result of that can be anything.
The fact that the Visual C++ compiler does not diagnose void main
is just another compiler bug.
change
DataOutput *output(file.CreateOutput(false, false));
to
DataOutput* output = file.CreateOutput(false, false); and it might work. But to make this a reasonable lib function you don't have to clean up after you should not return a pointer but an actual object.