auto [value, text, goose] = std::scan<int, std::string, Goose>(input, "{} {} {}");
A halfway solution would be to have the hypothetical std::scan() take references to std::optional<>s or std::expected<>s: std::optional<int> value;
std::optional<std::string> text;
std::optional<Goose> goose;
/* auto result = */ std::scan(input, "{} {} {}", value, text, goose);
The latter would be type safe, close to how scanf() works, but less satisfying from a functional programming standpoint. template<>
struct std::scanner<Goose> {
constexpr auto parse(std::format_parse_context& ctx) {…}
auto scan(std::format_context& ctx) const -> std::optional<Goose> {…}
}; if (sscanf(user_input, "%4u-%2u-%2u", &year, &month, &day) != 3) {
// return an error
}
This still does not catch trailing garbage, but you could check for that as well: if (sscanf(user_input, "%4u-%2u-%2u%c", &year, &month, &day, &dummy) != 3) {
// return an error
}
The result would be 4 if there was at least one trailing character. Too bad there is still no std::scan() companion to C++23's std::print().
A very good way to deal with this is to recognize that all infrastructure needs maintenance and periodic replacement. Once it is time to replace a road, that is when you change it to include bike paths, bus lanes and so on.
You need some foresight, planning, make new road designs part of law, and political will. Some countries have made this work very well.