2

我建立了一个Card重载<<运算符的小类,基本上打印出卡片的花色和价值。

实现细节与我想在这里提出的问题无关,只是假设显而易见。因为Card我建了一个类CardDeck。当然CardDeck可以用完卡。这促使我进行了这次尝试:

std::optional<Card> CardDeck::drawCard() {
    if (this->Cards.empty()) {
        return std::nullopt;
    }
    else {
        Card card = this->Cards.front();
        this->Cards.pop_front();
        return std::optional<Card>{card};
    }
}

现在可以抽一张牌,处理空牌的可能性是客户端代码使用的责任CardDeck,但该方法并不总是返回值是冗长的。我喜欢那个解决方案。

无论如何,我是 C++ 的新手,我做了一个天真的 ansatz:

std::cout<< cardDeck.drawCard().value_or("Out of cards!");

这会失败,因为"Out of cards!"ischar*但 not的类型Card

我的问题:有没有办法在不检查和访问 vaule/在两个单独的地方使用替换的情况下保护优雅的 oneliner?

4

4 回答 4

6

operator <<在我看来,解决这个问题的最简单方法是为for添加重载std::optional<Card>。在那里你可以处理打印内容的逻辑

std::ostream& operator <<(std::ostream& os, const std::optional<Card>& card)
{
    if (card == std::nullopt)
        return os << "Out of cards!";
    else
        return os << *card;
}

然后你就可以拥有

std::cout << cardDeck.drawCard();
于 2018-06-06T15:25:58.027 回答
4

您可能会为变体编写通用辅助函数std::visit,但对于std::optional,类似于:

template <typename T, typename F, typename FEmpty>
auto my_optional_visit(const std::optional<T>& o, F f, FEmpty fEmpty)
{
    return o ? f(*o) : fEmpty();
}

接着:

my_optional_visit(cardDeck.drawCard(),
                  [](const Card&c){ std::cout << c; },
                  [](){ std::cout << "Out of Cards!" });
于 2018-06-06T15:32:15.950 回答
1

我会使用一个函数(命名空间范围或卡的静态成员),它接受 astd::optional<Card>并将其文本表示作为字符串返回。相关位将是:

class Card {
    ...
    std::string repr() const {
        std::string cr;
        // writes the textual representation of a card here
        return cr;
    }
    static std::string repr(const std::optional<Card> &card) {
        if (card.has_value()) {
            return card.value().repr();
        }
        return "Out of cards!";
    }
};

std::ostream& operator << (std ostream& out, const Card& card) {
    out << card.repr();
}

然后,您只需使用:

std::cout << Card::repr(cardDeck.drawCard());
于 2018-06-06T15:40:28.933 回答
0

C++ 函数只能有一种返回类型。在您的情况下,您希望在运行时确定结果的类型,每次调用时它都可能不同。那是不可能的。

如果你需要经常这样做,我建议只创建一个小的包装函数。

std::string optionalCardToString(const std::optional<Card> & card,
                                 const std::string & emptyString) {
    return card ? card->to_string() : emptyString;
}

std::cout << optionalCardToString(cardDeck.drawCard(), "Out of Cards");

如果性能是一个问题,你可以做很多事情来提高效率以避免复制,但总体思路就在那里。

我应该注意,在现实世界的场景中,您不太可能Card从中绘制 aCarDeck并将其通过管道传输到cout. 您通常还想对卡片执行其他操作,例如将其添加到 aHand或 a Pile。这种需要做一些其他事情可以消除您对必须多次提及该值的担忧。

于 2018-06-06T15:46:28.307 回答