VC选择了错误的运算符<<仅在第一次调用时重载。漏洞?

发布于 2024-08-16 23:12:18 字数 2475 浏览 7 评论 0 原文

我花了一些时间删除所有不影响的代码,这是我的问题。

--- 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);
}

< em>--- 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" 并运行

--- test.txt ---

00414114test

基本上发生的情况是第一个 运算符<<< main 中的 /code> 调用绑定到成员方法

basic_ostream<char>& basic_ostream<char>::operator<<(
    const void *)

,而第二个调用(正确)绑定到

basic_ostream<char>& __cdecl operator<<(
    basic_ostream<char>&,
    const char *)

成员方法,从而给出不同的输出。

如果我执行以下任一操作,则不会发生这种情况:

  • 内联 File::CreateOutput
  • DataOutput 更改为带有 Element=char 的非模板>
  • 在第一个运算符<<之前添加*output; call

我认为这是不受欢迎的编译器行为是否正确?

对此有什么解释吗?

哦,我现在正在使用 VC7 来测试这个简化的代码,但我已经尝试了 VC9 和 VC8 中的原始代码,并且发生了同样的事情。

任何帮助甚至线索都表示赞赏

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 with Element=char
  • Add *output; before the first operator<< 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

彩虹直至黑白 2024-08-23 23:12:18

这是一个编译器错误(不仅仅是看起来像一个),因为它为两个相同的语句生成不同的调用绑定

    *output << "test"; // Calls wrong overload
    *output << "test"; // Calls right overload!!!

但是,编译器有权利这样做,因为 这意味着

    void main()

这不是一个有效的 C++ 程序(C 中也不允许使用 void main,并且它在 C 或 C++ 中从来都不是有效的)。所以,你一直运行编译无效源代码的结果。其结果可以是任何东西。

Visual C++ 编译器不诊断 void main 的事实只是另一个编译器错误

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.

浅笑依然 2024-08-23 23:12:18

看起来像一个编译器错误。您可能想尝试使用最新的 VC 编译器(目前是 VC10 Beta2),如果它没有修复,请与 VC 团队跟进(您将需要一个完整的独立存储库)。如果它已修复,您应该使用您找到的周围的工作并继续您的生活。

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.

蒲公英的约定 2024-08-23 23:12:18

更改

DataOutput *output(file.CreateOutput(false, false));

DataOutput* 输出 = file.CreateOutput(false, false);它可能会起作用。但是为了使其成为一个合理的库函数,您不必在返回实际对象而不是指针后进行清理。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文