【问题标题】:Example from book failing to compile, converting ifstream into bool书中示例无法编译,将 ifstream 转换为 bool
【发布时间】:2017-10-10 22:47:57
【问题描述】:

我是 C++ 的学生。我正在阅读这本书,“从 C++ 早期对象开始(第 9 版)。第 6 章(关于函数)中的示例 27 从文件中读取数据但不会编译。下面是完整代码:

// Program 6-27
#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
using namespace std;

// Function prototype
bool readData(ifstream &someFile, string &city, double &rain);

int main()
{
    ifstream inputFile;
    string city;
    double inchesOfRain;

    // Display table headings
    cout << "July Rainfall Totals for Selected Cities \n\n";
    cout << " City      Inches \n";
    cout << "_________________ \n";

    // Open the data file
    inputFile.open("rainfall.dat");
    if (inputFile.fail())
    cout << "Error opening data file.\n";
    else
    {
        // Call the readData function
        // Execute the loop as long as it found and read data
        while (readData(inputFile, city, inchesOfRain) == true)
        {
            cout << setw(11) << left << city;
            cout << fixed << showpoint << setprecision(2)
                << inchesOfRain << endl;
        }
        inputFile.close();
    }
    return 0;
}

bool readData(ifstream &someFile, string &city, double &rain)
{
    bool foundData = someFile >> city >> rain;
    return foundData;
}

这是数据文件 Rainfall.dat 的随附数据:

Chicago 3.70
Tampa 6.49
Houston 3.80

问题在于“bool readData”函数中的这一行:

bool foundData = someFile >> city >> rain;

我正在使用 Visual Studio Community 2017。“someFile”有一条红色波浪线,下拉菜单显示以下错误:

不存在从“std::basic_istream&lt;char, std::char_traits&lt;char&gt;&gt;”到“bool”的合适转换函数

我不太明白错误信息,但我设法让这个程序正常工作:

一个简单的演员表:

bool readData(ifstream &someFile, string &city, double &rain)
{
    return static_cast<bool>(someFile >> city >> rain);
}

或者这个作为替代:

bool readData(ifstream &someFile, string &city, double &rain)
{
    if(someFile >> city >> rain)
        return true;
    else
        return false;
}

所以,我真正的问题是:

  • 我的解决方案是否可行或有更好的方法?
  • 为什么在您的教育材料上出现错误? 可以想象应该首先经过彻底的测试。或者这是 只是 Visual Studio (intelliSense) 特定的,但在 其他编译器?

【问题讨论】:

  • 请以逐字文本而不是图片的形式发布错误消息!
  • 只需使用return someFile &gt;&gt; city &gt;&gt; rain; 而不是这个多余的if() else 构造。不需要static_cast
  • 谢谢。我实际上尝试了 return someFile >> city >> rain;首先,但仍然得到红色波浪。只有演员才能将其移除!
  • 你可能想试试a more recent book
  • 离题 - 但我发现自己想知道为什么 readData 需要 ifstream 而不是接受任何 istream (如果您希望能够从 @ 阅读,那会更好987654335@ 或 istringstream 代替)。然后,正如其中一个答案所暗示的那样,我倾向于让它返回 istream&amp; 而不是 bool

标签: c++ visual-studio-2017 intellisense


【解决方案1】:

流有一个成员

explicit operator bool() const;

这使得它可以转换为bool 值,但由于运算符是explicit,这仅适用于需要布尔值的上下文。

您已经发现这包括 if 语句和显式转换。它不包括其他类型的表达式,例如赋值。

最初(C++98)操作符不是explicit(因为还没有发明这样的东西)所以代码示例可能在当时可以工作。这部分的书好像没有更新。

【讨论】:

  • 没有真正回答,为什么 visual-studio-2017 智能感知渲染会抱怨。
  • @user0042 为什么不呢?代码不是有效的 C++11 或 Visual Studio 2017 试图实现的任何标准,因此智能感知在代码下放置红色波浪线是应该发生的。
  • 大家。供参考。这本书显然是 2017 年出版的最新版本,应该符合 C++11。
  • @Webbmaster1 好吧,all the 专业 compilers 不同意 with the 书。你可以搜索那本书的勘误表。
  • @Webbmaster1 - 书中也有错误。不只是在程序中。
【解决方案2】:

我会考虑

  • 返回 std::ios&amp; 以推迟到 bool 的上下文转换

    std::ios& readData(std::ifstream &someFile, std::string &city, double &rain) {
        return someFile >> city >> rain;
    }
    

    结果是您可以像在路上一样简单地使用它:

    if (readData(file, city, rain)) {
        // ...
    }
    

    界面将编译只包含#include &lt;iosfwd&gt;


  • 手动触发上下文转换:

    bool readData(std::ifstream &someFile, std::string &city, double &rain) {
        return bool{someFile >> city >> rain};
    }
    

【讨论】:

    【解决方案3】:

    流的operator bool 在基类basic_ios 中声明如下

    explicit operator bool() const;
    ^^^^^^^^
    

    所以没有从std::basic_ifstreambool 类型的隐式转换。

    这个解决方案

    bool readData(ifstream &someFile, string &city, double &rain)
    {
        return static_cast<bool>(someFile >> city >> rain);
    }
    

    看起来不错。

    您还可以通过以下方式使用逻辑否定运算符的技巧

    bool readData(ifstream &someFile, string &city, double &rain)
    {
        return !!(someFile >> city >> rain);
    }
    

    因为根据 C++ 标准(5.3.1 一元运算符)

    9 逻辑否定运算符的操作数!是上下文 转换为 bool(第 4 条);如果转换后,它的值为真 操作数为假,否则为假。结果的类型是bool。

    虽然我认为第一个函数实现更具可读性。

    【讨论】:

    • MSVC 甚至不再支持 C++14 之前的标准,所以不妨好好利用 C++14 的特性。那么如何将readData 的返回类型说明符更改为decltype(auto)? (缺点当然是这样的函数不能前向声明,所以需要上移,还要去掉== true。)
    【解决方案4】:

    我想他们直到本书后面才会涉及它,但我会以不同的方式处理这个问题。我将首先定义一个结构来将城市的名称和降雨量放在一起:

    struct precipitation { 
        std::string location;
        double amount;
    };
    

    然后我会定义operator&gt;&gt; 的重载以从流中提取该类型的对象:

    std::istream &operator>>(std::istream &is, precipitation &p) { 
         return is >> p.location >> p.amount;
    }
    

    这几乎是流提取器的标准形式——获取流的引用和被提取类型的对象的引用,然后返回流(同样,通过引用)。

    这让你读起来相当干净:

    precipitation precip;
    
    std::ifstream in("rainfall.dat");
    
    while (in >> precip)
        std::cout << "City: " << precip.location << ", amount: " << precip.amount << "\n";
    

    如果您想知道它是如何工作的:流对象支持转换为布尔值,当从流中读取成功时会生成 true,当读取失败时会生成 false,所以它会从流中读取直到读取失败,然后停止。

    这也是流迭代器所期望的形式,因此您也可以使用它们。例如,要将文件的全部内容读入向量,您可以执行以下操作:

    std::vector<precipitation> data { std::istream_iterator<precipitation>(in),{}};
    

    【讨论】:

    • 你神奇地读懂了我的心思。起初,我的答案中有precipation这个词!我们一定是连体双胞胎,出生时就分开了。或者其他的东西。 (我删除了它,因为它对这个问题并不重要。仍然 +1 为教育里程!)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    相关资源
    最近更新 更多