kc9jud
kc9jud

Reputation: 402

Stream insertion operator >> for std::optional

Is there a reasonably idiomatic way* to use stream insertion operators >> with std::optional? The standard class does not provide an overload for std::istream& operator>>(std::istream& is, std::optional<T>& obj), and I believe using

std::optional<int> i{};
stream >> *i;

invokes undefined behavior if i does not already have a value.

*Yes I am aware that nothing about streams is idiomatic or natural...

Upvotes: 2

Views: 211

Answers (2)

kc9jud
kc9jud

Reputation: 402

Inspired by @Jan Schultke's answer, one could also provide a definition for operator>>:

template<typename T>
std::istream& operator>>(std::istream& is, std::optional<T>& obj)
{
    if (T result; is >> result) obj = result;
    else obj = {};
    return is;
}

//...
std::optional<int> i;
std::cin >> i;

However, it is unclear to me if defining such a function which acts purely on Standard Library types is allowed by the Standard. (At least here operator>> is not in the std:: namespace.) It seems that doing this is allowed, but may be a questionable practice.

Upvotes: 1

Jan Schultke
Jan Schultke

Reputation: 39804

You're right. *i is undefined behavior for an empty std::optional. What you should do instead is:

auto i = [&stream] -> std::optional<int> {
    int result;
    if (stream >> result) return result;
    else return {};
}();

Instead of an immediately-invoked lambda expression (IILE), you could also write a utility function that does this for you:

template <typename T>
std::optional<T> try_read(std::istream& stream) {
    T result;
    if (stream >> result) return result;
    else return {};
}

// ...
std::optional<int> i = try_read<int>(stream);

Upvotes: 1

Related Questions