Альтернатива value_or, которая безразлична к возвращаемому типу

Я построил маленький класс 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 ++ я сделал наивный анзац:

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

Это терпит неудачу как тип "Out of cards!" является char* но нет Card,

Мой вопрос: есть ли способ сохранить элегантный вкладыш без проверки и доступа к вауле / с помощью замены в двух отдельных местах?

2

Решение

Самый простой способ справиться с этим в моем мнении — добавить перегрузку для 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();
6

Другие решения

Вы могли бы написать универсальную вспомогательную функцию а-ля 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!" });
4

Я хотел бы использовать функцию (либо пространство имен области или статический член карты), который принимает 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());
1

Функция 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, Это нужно сделать что-то еще, что снимает с вас беспокойство о необходимости упоминать стоимость более одного раза.

0
По вопросам рекламы [email protected]