c++ 难以为构造函数正确格式化变量

ego6inou  于 2023-03-05  发布在  其他
关注(0)|答案(1)|浏览(126)

我想通过一个构造函数传递两个值,但是我很难找到一种方法来定义我的变量,这样构造函数就可以接受这些值,构造函数本身必须保持不变。
我要传入的构造函数是:

playing_card(card_face const& f, std::optional<card_suit> const& s = std::nullopt) : face_val(f), suit_val(s)
{
  if (!s.has_value() && f != card_face::white_joker && f != card_face::red_joker)
    throw std::domain_error("playing card without suit must be a joker");     
};

而给我错误的部分,我试图通过:

std::optional<playing_card> read_playing_card(std::istream& is)
{
  std::optional<playing_card> retval;
  
  auto face = read_card_face(is);
  //auto suit;
  
  if (face.has_value() && face != card_face::white_joker && face != card_face::red_joker)
  {
    auto suit = read_card_suit(is);
    
    if (suit.has_value())
    {
      retval = playing_card(face, suit);
    }
   
    retval = playing_card(face);    
  }

  if (!retval.has_value())
    is.setstate(std::ios_base::failbit);
}

错误为:

a4-provided.cpp:271:39: error: no matching function for call to ‘playing_card::playing_card(std::optional<card_face>&, std::optional<card_suit>&)’

  271 |       retval = playing_card(face, suit);

我明白编译器在说什么,但是我不确定如何在代码中导航这个错误。
整个街区都在这里:

//=============================================================================
#include <array>
#include <istream>
#include <optional>
#include <string>
#include <sstream>

#include "a4-provided.hpp"

//=============================================================================
//
// std::optional<card_suit> read_card_suit(std::istream)
//
// read_card_suit reads a card suit from an input stream returning a
// an optional value. The return value only holds a card_suit if a valid 
// card_suit was read in. If an error occurs on the stream while reading in
// the data, then stream is failed. If the character on the stream read in
// is not a valid suit, then the character read in is returned to the stream
// using is.unget().
//
std::optional<card_suit> read_card_suit(std::istream& is)
{
  std::optional<card_suit> retval;
  auto ch = is.get();

  if (!std::istream::traits_type::eq_int_type(ch,std::istream::traits_type::eof()))
  {
    switch (ch)
    {
      case 'c':
        retval = card_suit::clubs;
        break;

      case 'd':
        retval = card_suit::diamonds;
        break;

      case 'h':
        retval = card_suit::hearts;
        break;

      case 's':
        retval = card_suit::spades;
        break;

      default:
        is.unget();                               // return ch to stream
        is.setstate(std::ios_base::failbit);      // fail the stream
    }
  }
  else
    is.setstate(std::ios_base::failbit);          // fail the stream

  return retval;
}

//=============================================================================
//
// Provide a simple way to generate all playing cards without requiring 
// card_face, card_suit, playing_card, and all supporting operations to be
// written first.
//
std::string all_playing_cards_as_string()
{
  std::array<char,4> const special_face{ 'A', 'J', 'Q', 'K' };
  std::array<char,4> const suit{ 'c', 'd', 'h', 's' };
  std::ostringstream buf;

  // generate all numeric face cards...
  for (int face=2; face != 11; ++face)
    for (auto const s : suit)
      buf << face << s;

  // generate all special face cards...
  for (auto const f : special_face)
    for (auto const s : suit)
      buf << f << s;

  // generate two jokers (red and white)...
  buf << "RW";

  // return string...
  return buf.str();
}

//=============================================================================
std::optional<card_face> read_card_face(std::istream& is)
{
  std::optional<card_face> retval;
  
  //retrieve char from input stream
  auto ch = is.get();
  
  //peek at the next char
  auto ch2 = is.peek();
  
  //is ch EOF ? if no, continue
  if (!std::istream::traits_type::eq_int_type(ch,std::istream::traits_type::eof()))
  {
    switch (ch)
    {
      case 'A':
        retval = card_face::ace;
        break;
      
      case '2':
        retval = card_face::two;
        break;
      
      case '3':
        retval = card_face::three;
        break;
      
      case '4':
        retval = card_face::four;
        break;
      
      case '5':
        retval = card_face::five;
        break;
      
      case '6':
        retval = card_face::six;
        break;
      
      case '7':
        retval = card_face::seven;
        break;
      
      case '8':
        retval = card_face::eight;
        break;
      
      case '9':
        retval = card_face::nine;
        break;
      
      case 'J':
        retval = card_face::jack;
        break;
      
      case 'Q':
        retval = card_face::queen;
        break;
      
      case 'K':
        retval = card_face::king;
        break;
      
      case 'R':
        retval = card_face::red_joker;
        break;
      
      case 'W':
        retval = card_face::white_joker;
        break;
      
      case '1':
        //check if ch2 = 0
        if (ch2 == '0')
        {
          is.ignore();
          retval = card_face::ten;
        }
        else
          is.unget();
        is.setstate(std::ios_base::failbit);          
        break;
        
      default:
        is.unget();
        is.setstate(std::ios_base::failbit);
        break;      
    }
  }
  else
    is.setstate(std::ios_base::failbit);
  
  if (retval.has_value())
    return retval;  
}

inline std::istream& operator>>(std::istream& is, card_face& s)
{
  auto face = read_card_face(is);
  if (face)
    s = *face;
   return is;
}

inline std::ostream& operator<<(std::ostream& os, card_face const& s)
{
  switch (s)
  {
    case card_face::ace:           os.put('A'); break;
    case card_face::two:           os.put('2'); break;
    case card_face::three:         os.put('3'); break;
    case card_face::four:          os.put('4'); break;
    case card_face::five:          os.put('5'); break;
    case card_face::six:           os.put('6'); break;
    case card_face::seven:         os.put('7'); break;
    case card_face::eight:         os.put('8'); break;
    case card_face::nine:          os.put('9'); break;
    case card_face::ten:           os.put('1'); os.put('0'); break;
    case card_face::jack:          os.put('J'); break;
    case card_face::queen:         os.put('Q'); break;
    case card_face::king:          os.put('K'); break;
    case card_face::white_joker:   os.put('W'); break;
    case card_face::red_joker:     os.put('R'); break;
    default: 
      os.setstate(std::ios_base::failbit); 
      break;
  }
  return os;
}

class playing_card
{
  private:
    card_face face_val;
    std::optional<card_suit> suit_val;
    
  public:
    playing_card() = delete;
    
    playing_card(card_face const& f, std::optional<card_suit> const& s = std::nullopt) : face_val(f), suit_val(s)
    {
      if (!s.has_value() && f != card_face::white_joker && f != card_face::red_joker)
        throw std::domain_error("playing card without suit must be a joker"); 
    };
    
    const std::optional<card_face> face() { return face_val; }
    
    const bool has_suit() { return suit_val.has_value(); }
    
    const std::optional<card_suit> suit() 
    {
      if (has_suit() == true) 
        return suit_val; 
    }
    
    friend bool operator==(playing_card const&, playing_card const&);
    friend std::strong_ordering operator <=> (playing_card const&, playing_card const&);
};

std::optional<playing_card> read_playing_card(std::istream& is)
{
  std::optional<playing_card> retval;
  
  auto face = read_card_face(is);
  //auto suit;
  
  if (face.has_value() && face != card_face::white_joker && face != card_face::red_joker)
  {
    auto suit = read_card_suit(is);
    
    if(suit.has_value())
    {
      retval = playing_card(face, suit);
    }
   
    retval = playing_card(face);
  }

  if (!retval.has_value())
    is.setstate(std::ios_base::failbit);
}

我尝试过手动定义facesuit,尝试过将const添加到变量中,尝试过混淆指针和引用,但我似乎不明白如何按照编译器想要的方式格式化它。

o7jaxewo

o7jaxewo1#

编译器抱怨您将2个std::optional对象传递给playing_card构造函数,而它在第2个参数中只接受1个std::optional
更改此行:

retval = playing_card(face, suit);

改为:

retval = playing_card(face.value(), suit);

但是,该语句是无用的,因为您在调用retval = playing_card(face)之后立即调用,从而丢弃了前一语句刚刚创建的对象。我怀疑您在if上缺少了一个else,例如:

if (suit.has_value())
{
  retval = playing_card(face.value(), suit);
}
else // <-- here!
  retval = playing_card(face.value());

在这种情况下,您根本不需要if,只需无条件地调用retval = playing_card(face.value(), suit); *。
话虽如此:
read_playing_card()read_card_face()playing_card::suit()都表现出undefined behavior,因为它们被声明为返回std::optional对象,但并非所有代码路径都实际返回return这样的对象。编译器应该警告您这个错误。
还有,这种代码:

auto ch = is.get();

if (!std::istream::traits_type::eq_int_type(ch,std::istream::traits_type::eof()))

可以大大简化为(与is.peek()相同):

auto ch = is.get();

if (ch != std::istream::traits_type::eof())

或者甚至是这样:

char ch;

if (is.get(ch))

同样,playing_card::face()返回std::optional也没有意义,因为返回的值不能为空,因此read_card_face()返回std::optional也没有意义,每张牌都需要一张脸,read_card_face()的调用者应该通过查看std::istream的失败状态来检查读取失败,而不是函数是否返回了一个可选的(但不是真的!)face。
此外,playing_card() = delete;是不必要的,因为playing_card有一个用户定义的构造函数,所以编译器不会生成自己的默认构造函数。

相关问题