我已经构建了一个小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!"
的类型为char*
而非Card
,因此失败。
我的问题:有没有办法保证优雅的oneliner没有检查和访问vaule /在两个单独的地方使用替换?
答案 0 :(得分:6)
在我的选择中处理此问题的最简单方法是为operator <<
添加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();
答案 1 :(得分:4)
您可以为变体编写通用辅助函数la 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!" });
答案 2 :(得分:1)
我会使用一个函数(命名空间作用域或卡的静态成员),它接受std::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());
答案 3 :(得分: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
中提取CarDeck
并将其传输到cout
。您通常也希望使用该卡执行其他操作,例如将其添加到Hand
或Pile
。这需要做一些其他事情,否定你不得不多次提到价值的担忧。