VC が間違った演算子<< を選択すると、最初の呼び出し時にのみオーバーロードが発生します。バグ?
-
21-09-2019 - |
質問
時間をかけて影響のないコードをすべて削除しましたが、ここに問題があります。
--- ファイル.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;
};
--- ファイル.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;
}
そして、これはビルド後の出力ファイルです cl
とオプション /D "WIN32" /D "_UNICODE" /D "UNICODE"
そして走っています
--- テスト.txt ---
00414114test
基本的に何が起こるかというと、最初は operator<<
呼び戻す main
メンバーメソッドにバインドされています
basic_ostream<char>& basic_ostream<char>::operator<<(
const void *)
一方、2 番目のものは (正しく) バインドされています
basic_ostream<char>& __cdecl operator<<(
basic_ostream<char>&,
const char *)
したがって、異なる出力が得られます。
次のいずれかを実行すると、これは発生しません。
- 列をなして
File::CreateOutput
- 変化
DataOutput
非テンプレートの場合Element=char
- 追加
*output;
最初の前にoperator<<
電話
これはコンパイラの望ましくない動作であると考えるのは正しいでしょうか?
これについて何か説明はありますか?
ああ、私は現在この簡略化されたコードをテストするためにVC7を使用していますが、VC9とVC8で元のコードを試してみましたが、同じことが起こりました。
助けやヒントでも大歓迎です
解決
コンパイラのバグのように見えます。あなたは(現時点ではVC10ベータ2である)最新のVCのコンパイラで試してください、そして、それが固定されていないなら、(あなたが完全な自己完結型のレポが必要です)VCチームをフォローアップすることがあります。それが固定されている場合、あなたはちょうどあなたが見つけ周りの作品を使用して、あなたの生活に移動する必要があります。
他のヒント
2 つの同一のステートメントに対して異なる呼び出しバインディングが生成されるため、これはコンパイラのバグです (見た目だけではありません)。
*output << "test"; // Calls wrong overload
*output << "test"; // Calls right overload!!!
しかし、 コンパイラはその権利の範囲内にあります あなたが持っているので、これを行うには
void main()
これは、これが有効な C++ プログラムではないことを意味します (void main
C でも許可されておらず、C や C++ でも有効になったことはありません)。つまり、無効なソースコードをコンパイルした結果を実行していることになります。その結果は何でも構いません。
Visual C++ コンパイラが診断を行わないという事実 void main
ちょうどです 別のコンパイラのバグ.
変更
DataOutput内*出力(file.CreateOutput(偽、偽));
と
DataOutput内*出力= file.CreateOutput(偽、偽)。そしてそれがうまくいくかもしれません。しかし、この合理的なlibの関数を作るために、あなたは、ポインタが、実際のオブジェクトを返すべきではないの後にクリーンアップする必要はありません。