【问题标题】:VC choosing the wrong operator<< overload only at the first call. Bug?VC 仅在第一次调用时选择了错误的 operator<< 重载。漏洞?
【发布时间】:2009-12-28 20:04:26
【问题描述】:

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

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

这是使用cl 和选项/D "WIN32" /D "_UNICODE" /D "UNICODE" 构建并运行后的输出文件

--- test.txt ---

00414114test

基本上发生的情况是main 中的第一个operator&lt;&lt; 调用绑定到成员方法

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

而第二个(正确)绑定到

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

从而给出不同的输出。

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

  • 内联File::CreateOutput
  • DataOutput 更改为非模板的Element=char
  • 在第一个 operator&lt;&lt; 调用之前添加 *output;

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

对此有什么解释吗?

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

感谢任何帮助甚至线索

【问题讨论】:

  • 这是一个明确的编译器错误。顺便说一句,它也可以在 VS2010 beta2 上重现。正如所写,这已经是一个很棒的错误报告——你可以将它发布到connect.microsoft.com/VisualStudio/feedback
  • 我会尽快的。至少我没有白白浪费这些时间

标签: c++ visual-c++ operator-overloading c++-standard-library


【解决方案1】:

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

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

但是,编译器有权这样做,因为您有

    void main()

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

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

【讨论】:

  • +1 for “Visual C++ 编译器不诊断 void main 的事实只是另一个编译器错误。”
  • 对不起,这是错误的。该代码不可移植,但 Visual C++ 文档明确声明它是允许的 (msdn.microsoft.com/en-us/library/6wd819wh.aspx) 并且 C++ 标准明确允许实现接受 main 以及除 @987654327 之外的类型@ 或 int(int, char*[])。微软可以说它超越了标准的祝福,但他们并没有破坏有效的代码,所以它是一个合法的扩展。该代码不符合 all 独立 C++ 实现,但在此特定编译器上是正确的。
  • @BenVoigt:对不起,你错了。首先,实际意义:代码不会编译,例如克++。如果 Visual c++ 编译器首先发出诊断,它确实可以解决任何问题。这就是标准对它的所有要求,但是,它没有做到这一点。因此,在这方面它不符合标准。
  • @BenVoigt:此外,您认为 Visual c++ 不是托管实现,而是独立实现。我想你可能不知道这意味着什么。但是 FIY,如果是真的,那么这将比对main 过于宽容要可耻得多... :-( 我更喜欢视觉 C++,我认为这个假装它是完美的粉丝的东西,只是与任何事情都不太相关。我的建议是照原样。,
  • @Alf:好吧,我把独立和托管搞混了。并且不需要为不可移植的代码生成警告。例如,您不会争辩说int main(int argc, char* argv[], char* envp[]) 需要警告吗?
【解决方案2】:

看起来像一个编译器错误。您可能想尝试使用最新的 VC 编译器(目前是 VC10 Beta2),如果没有修复,请与 VC 团队联系(您需要一个完整的自包含 repo)。如果解决了,你应该使用你找到的周围的工作并继续你的生活。

【讨论】:

  • FWIW 我也回购了最新的 VC10 位。看起来像一个编译器错误。
【解决方案3】:

改变

数据输出 *output(file.CreateOutput(false, false));

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

【讨论】:

  • 标准流类是不可复制的(std::basic_ios 具有私有复制构造函数和复制赋值运算符),这意味着使用标准流(或任何在相同方法中构建的流)返回流价值本身不是一种选择。智能指针或包装器可以在这里工作,但这可能是另一回事,具体取决于 OP 的设计。
  • 这在 VC7 中不起作用。但是有很多解决方法,比如在两个调用之前添加 *outputoutput-&gt;SomeMethod() 似乎都可以。至于设计部分,这不是实际代码,这是我尝试重现错误。在原始代码中,相同的方法返回一个指向基类的智能指针,并且该方法本身是虚拟的,允许 File 以外的类返回不同的流派生对象。
猜你喜欢
  • 2011-02-06
  • 2011-06-10
  • 2013-08-11
  • 2017-09-26
  • 1970-01-01
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
  • 2019-11-28
相关资源
最近更新 更多