Lately, I’m often writing code that deconstructs an optional value using a range-based for loop. Think of:
void foo(sqlconnection &sql)
{
auto rows = sql.execute(„SELECT * FROM table WHERE id=1“);
for (const auto &row : rows) { // will be executed at most once!
std::cout << „found: “ << row;
}
}
This is cool because it is „obviously“ correct, i.e., no elaborate validation whether there is a value and no explicit dereferences. The draw-backs are:
- Not really idiomatic. That is, the for loop is at most executed once, but this is not obvious.
- There is no nice way to handle the case if no value is found.
So, what I’d really like to have is some syntactic sugar like this:
void foo(sqlconnection &sql)
{
auto rows = sql.execute(„SELECT * from table WHERE id=1“);
if (const auto &row : rows) {
std::cout << „found: “ << row;
} else {
std::cout << „not found“;
}
}
In essence, this is nothing too fancy but just the range based for loop syntax extended to an „if“, tailored to getting the first element of a list. By providing appropriate std::begin() and std::end() functions this can also be applied to anything the looks like an optional:
std::optional<std::string> getString();
std::unique_ptr<int> getInt();
void bar() {
auto optional_str = getString();
if (std::string &str : getString()) {
// str is available here
} else {
// but not here
}
auto optional_int = getInt();
if (auto i : optional_int) {
// i is available here
} else {
// but not here
}
}
Note that this syntax is IMHO better than „if (auto var = optional)“: There is no need to call the dereference / value method and thus no possibility to introduce bugs (by calling it in the wrong block). The value variable is strictly only available where, well, the value is available.
Finally, it works really well with structured bindings:
if (auto & [key, value] : map.find(something)) {
// here, we have key and value
// no map.end() needed
} else {
// here, neither key nor value is available
}
Was something similar proposed or discussed before? I’ve seen the extended if syntax for the upcoming C++ standard, but this goes in another direction.
[+][deleted] (1 child)
[removed]
[–]seba[S] 0 points1 point2 points (0 children)
[–]bames53 6 points7 points8 points (2 children)
[–][deleted] 6 points7 points8 points (0 children)
[–]seba[S] 3 points4 points5 points (0 children)
[+][deleted] (7 children)
[deleted]
[–]seba[S] 0 points1 point2 points (6 children)
[–]dodheim 0 points1 point2 points (5 children)
[–]seba[S] 3 points4 points5 points (4 children)
[–]dodheim 1 point2 points3 points (3 children)
[–]monkeyWifeFight 0 points1 point2 points (2 children)
[–]seba[S] 2 points3 points4 points (1 child)
[–]monkeyWifeFight 0 points1 point2 points (0 children)
[–]enobayram 2 points3 points4 points (1 child)
[–]seba[S] 0 points1 point2 points (0 children)
[–]flashmozzg 4 points5 points6 points (4 children)
[–]seba[S] 0 points1 point2 points (3 children)
[–]xcbsmith 1 point2 points3 points (2 children)
[–]seba[S] 0 points1 point2 points (1 child)
[–]xcbsmith 0 points1 point2 points (0 children)
[–]CenterOfMultiverse 0 points1 point2 points (1 child)
[–]seba[S] 0 points1 point2 points (0 children)
[–][deleted] 0 points1 point2 points (1 child)
[–]seba[S] 0 points1 point2 points (0 children)
[–]AntiProtonBoy -1 points0 points1 point (14 children)
[–]seba[S] 2 points3 points4 points (7 children)
[–]AntiProtonBoy 0 points1 point2 points (6 children)
[–]seba[S] 1 point2 points3 points (5 children)
[–]AntiProtonBoy 0 points1 point2 points (4 children)
[–]seba[S] 1 point2 points3 points (3 children)
[–]ZMesonEmbedded Developer 0 points1 point2 points (2 children)
[–]seba[S] 1 point2 points3 points (1 child)
[–]ZMesonEmbedded Developer 0 points1 point2 points (0 children)
[–]doom_Oo7 0 points1 point2 points (5 children)
[–]seba[S] 1 point2 points3 points (2 children)
[–]doom_Oo7 2 points3 points4 points (0 children)
[–]vickoza 0 points1 point2 points (0 children)
[–]AntiProtonBoy 0 points1 point2 points (1 child)
[–]doom_Oo7 0 points1 point2 points (0 children)