C++;流提取运算符重载 这是一个关于用户编写C++ C++流抽取算子(>)的哲学(规范设计)问题。p>

C++;流提取运算符重载 这是一个关于用户编写C++ C++流抽取算子(>)的哲学(规范设计)问题。p>,c++,overloading,operator-keyword,C++,Overloading,Operator Keyword,假设在进入>>操作符实现时(对于用户编写的类),已经为输入流设置了eof标志 是否应该由用户编写提取运算符(>>) 设置失败标志(因为找不到所需对象的实例) 它是否应该返回到仍然设置了eof标志的调用方 如果使用第二种方法,则意味着调用方必须在尝试调用>>运算符之前始终检查eof标志。原因是>>运算符可能会成功提取所需类的实例并设置eof标志 原始代码如下。根据下面的注释,此代码似乎是错误的。如果输入上已经设置了eof,则提取操作符将简单地返回,eof仍然设置。如果设置了eof,但未设置bad和

假设在进入>>操作符实现时(对于用户编写的类),已经为输入流设置了eof标志

是否应该由用户编写提取运算符(>>)

  • 设置失败标志(因为找不到所需对象的实例)
  • 它是否应该返回到仍然设置了eof标志的调用方 如果使用第二种方法,则意味着调用方必须在尝试调用>>运算符之前始终检查eof标志。原因是>>运算符可能会成功提取所需类的实例并设置eof标志

    原始代码如下。根据下面的注释,此代码似乎是错误的。如果输入上已经设置了eof,则提取操作符将简单地返回,eof仍然设置。如果设置了eof,但未设置bad和fail,则应提取字符串以设置失败位。当然,可以直接设置失败位

    /* Implement the C/C++ >> (stream input) operator as a non-member 
       function */
    std::istream &operator>>(std::istream& is, DecNumber &val) {
      DecContext  context{DecContext::defInit};
      uint32_t    status;
      /* The true value below prevents whitespace from being skipped */
      std::istream::sentry  s(is, true);
      std::string           inStr;
      /* Check if the input stream is in a good state. Just return to the
         caller if the input stremm is not in a good state. The caller
         must handle this condition. */
      if(!s) 
        return is; 
      /* Get a string from the input stream. This string is converted to 
         a DecNumber below. Just return to the caller if this step causes
         any stream related errors. Note that reaching the end of the 
         input is not a stream related error here. A decimal number might
         be the absolute last thing in the stream. */
      is >> inStr;
      if (is.bad() || is.fail())
        return is;
      /* Try to convert the string to a DecNumber using the default context
         value */
      decNumberFromString(val.getDecVal(), inStr.c_str(), context.getDecCont());
      status = context.DecContextGetStatus();
      /* Remove a few status bits we don't care about */
      status &= ~(DEC_Inexact + DEC_Rounded);
      if (status)
        is.setstate(std::ios_base::failbit); 
      return is;
    }
    
    “如果使用第二种方法,则意味着调用方在尝试调用>>运算符之前必须始终检查eof标志。”

    不,你认为他们为什么要这么做

    “应该由用户编写的提取运算符(>>)设置失败标志(因为找不到所需对象的实例),还是只返回到仍设置了eof标志的调用方。”

    当然,后一个选项不应该在重载提取运算符中管理流状态,除非您添加自己的验证规则(例如,在
    std::string
    字段中需要特定的字符模式)。重载运算符使用的子提取操作通常可以正确执行

    假设您有如下内容:

    struct MyType {
        std::string field1;
        int field2;
        double field3;
    }
    
    std::istream& operator>>(std::istream& is, MyType& myinstance) {
          is >> field1;
          is >> field2;
          is >> field3;
          return is;
    }
    
    如果由于流处于
    eof()
    状态,因此
    运算符>>()
    失败,则每个提取操作都会将字段设置为其默认构造值,并且尝试提取的字段的值将保持在其原始状态

    实际上,我认为没有必要在重载的输入操作符中对
    eof()
    进行任何额外的检查,或者将流设置为
    fail()
    状态

    客户机(调用者)只需使用

     std::ifstream input("MyFile.txt");
     std::vector<MyType> allObjects;
     MyType curObject;
     while(input >> curObject) {
          allObjects.push_back(curObject);
     }
    
    std::ifstream输入(“MyFile.txt”);
    std::向量allObjects;
    MyType-curObject;
    while(输入>>curObject){
    allObjects.push_back(curObject);
    }
    

    您知道,无需在任何地方检查
    input.eof()

    您应该实现解决方案1

    当有疑问时,看看已经在做什么。正如您在下面所看到的,如果我们试图从EOF状态的流中读取数据,就会设置失败位

    请注意,EOF并不是唯一失败的方法。尝试设置
    std::string vals=“52 43 A”在下面的代码中

    如果由于任何原因
    操作员>
    实际上没有流式传输值,则应设置
    故障位。EOF只是其中一个原因

    #include <sstream>
    #include <iostream>
    #include <string>
    
    void print_stream (std::istream & print_me, int const & i)
    {
      std::cout << "i: " << i << "\n";
      std::ios_base::iostate bits = print_me.rdstate();
    
      std::cout << "good: " << (bits & std::ios_base::goodbit) << 
        ", bad: " << (bits & std::ios_base::badbit) << 
        ", fail: " << (bits & std::ios_base::failbit) <<
        ", eof: " << (bits & std::ios_base::eofbit) << "\n";
    
      std::cout << "\n----------------------------\n\n";
    }
    
    int main (void)
    {
      std::string vals = "52 43";
      std::istringstream iss(vals);
      int i;
    
      iss >> i;
      print_stream (iss, i);
      iss >> i;
      print_stream (iss, i);
      iss >> i;
      print_stream (iss, i);
      iss >> i;
      print_stream (iss, i);
    
      return 0;
    }
    

    请注意,典型的读取模式循环是

    while (input >> var >> var2 >> var3)
    {
      // failbit is not set.  All reads succeeded.
      // Do Stuff
    }
    
    如果您需要在读取多个值的过程中检测失败是否发生在某个点,那么是的,您需要更复杂一些,并进行一些测试,如

    while (true)
    {
      if (input >> var)
      {
        // We successfully read first value
        if (input >> var2 >> var3)
        {
          // We succesfully read all the values!
          // Do stuff
        }
        else
        {
          ErrorLog ("Partial line read!");
          break;
        }
      else
      {
        // Nothing else to read
        break;
      }
    }
    

    重载提取运算符不一定只是简单成员提取的集合。它可能需要进行一些自定义验证。如果验证失败,则应该在流上设置失败位。@BenjaminLindley好吧,OP应该进一步说明他们当时实际在做什么。我已经开始对这个问题进行近距离投票,主要是基于观点(稍后收回)。OP只是讨论如何处理
    eof()
    状态,在这种情况下,当然不需要操纵输入流状态。对操作性
    std::istream
    的自定义验证是操作重载运算符的流状态的有效原因。拥有
    eof()
    state,不是。我实际上只是回应了你答案中的一个特定点,“你不应该在重载提取操作符中管理流状态,它将正确地使用重载操作符使用的子提取操作。”——也就是说,依我之前的评论,imo是错误的。不管OP在做什么。πάνταῥεῖ, 为什么用户需要使用C++输入流提取算子(>)前后检查EOF?因为否则,用户将无法判断eof状态是否在使用>>运算符之前存在,或者eof状态是否由于使用>>运算符从输入流中获取值(很可能有效)而出现。@PeterSchaeffer这对用户来说真的很重要,为什么提取实际上失败了?如果是因为
    eof
    ,或者在解析流时实际上是提取失败?无论以哪种方式,流都会进入
    fail
    状态,除非您想对标准提取运算符中成功解析的字段进行额外验证。请写一篇文章来澄清您的实际要求!如前所述,我已经在这个问题上用尽了我的得票数,但除非你在你的问题中表明这一点,否则它要么过于宽泛,要么实际上是基于观点。πάνταῥεῖ, 我的目的是获取有关流提取操作符(>>)的规范forrm如何处理输入时存在或提取时发生的eof条件的建议。很多人(包括你)在解决这个问题上都很有帮助。这里没有规范形式,但是你有自定义重载的用例。对不起,C++没有提供烹饪配方,但灵活性太强。
    while (true)
    {
      if (input >> var)
      {
        // We successfully read first value
        if (input >> var2 >> var3)
        {
          // We succesfully read all the values!
          // Do stuff
        }
        else
        {
          ErrorLog ("Partial line read!");
          break;
        }
      else
      {
        // Nothing else to read
        break;
      }
    }