与返回类型无关的 value_or 的替代方案
Alternative to value_or that is indifferent to the returned type
我构建了一些 class Card
重载 <<
运算符,主要是打印花色和牌面值。
实现细节与我想在这里问的问题无关,只是假设显而易见。对于 Card
我建了一个 class 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
的客户端代码的责任,但是该方法并不总是 return 一个值是冗长的.我喜欢这个解决方案。
无论如何,作为 C++ 的新手,我做出了天真的回答:
std::cout<< cardDeck.drawCard().value_or("Out of cards!");
这失败了,因为 "Out of cards!"
的类型是 char*
而不是 Card
。
我的问题:有没有办法在不检查和访问 vaule / 在两个不同的地方使用替换件的情况下保护优雅的 oneliner?
在我看来,处理这个问题的最简单方法是为 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();
您可以为变体编写一个 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!" });
我会使用一个函数(命名空间作用域或卡的静态成员)将 std::optional<Card>
和 return 的文本表示形式作为字符串。相关位将是:
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());
一个 C++ 函数只能有一个 return 类型。在您的情况下,您希望在运行时确定结果的类型,每次调用时它都可能不同。那是不可能的。
如果您需要经常这样做,我建议您只创建一个小包装函数。
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");
如果性能是一个问题,您可以做很多事情来提高效率以避免复制,但总的想法是存在的。
我应该注意到,在现实世界中,您不太可能从 CarDeck
中抽取 Card
并将其通过管道传输到 cout
。您通常还想对卡片做其他事情,例如将其添加到 Hand
或 Pile
。这需要做一些其他事情来消除您对不得不多次提及该值的担忧。
我构建了一些 class Card
重载 <<
运算符,主要是打印花色和牌面值。
实现细节与我想在这里问的问题无关,只是假设显而易见。对于 Card
我建了一个 class 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
的客户端代码的责任,但是该方法并不总是 return 一个值是冗长的.我喜欢这个解决方案。
无论如何,作为 C++ 的新手,我做出了天真的回答:
std::cout<< cardDeck.drawCard().value_or("Out of cards!");
这失败了,因为 "Out of cards!"
的类型是 char*
而不是 Card
。
我的问题:有没有办法在不检查和访问 vaule / 在两个不同的地方使用替换件的情况下保护优雅的 oneliner?
在我看来,处理这个问题的最简单方法是为 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();
您可以为变体编写一个 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!" });
我会使用一个函数(命名空间作用域或卡的静态成员)将 std::optional<Card>
和 return 的文本表示形式作为字符串。相关位将是:
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());
一个 C++ 函数只能有一个 return 类型。在您的情况下,您希望在运行时确定结果的类型,每次调用时它都可能不同。那是不可能的。
如果您需要经常这样做,我建议您只创建一个小包装函数。
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");
如果性能是一个问题,您可以做很多事情来提高效率以避免复制,但总的想法是存在的。
我应该注意到,在现实世界中,您不太可能从 CarDeck
中抽取 Card
并将其通过管道传输到 cout
。您通常还想对卡片做其他事情,例如将其添加到 Hand
或 Pile
。这需要做一些其他事情来消除您对不得不多次提及该值的担忧。