In the following code fragment (I'm experimenting with it to understand how modern C++ is different):
auto input(auto p) {
std::cout << p << ' ';
long long i;
return
std::cin >> i
&& std::set<int>{EOF, '\n'}.contains(std::cin.get())
&& i >= 0
? std::optional{i}
: std::nullopt;
}
const auto input2() {
auto a = input("Enter the first operand:");
if (a) {
auto b = input("Enter the second operand:");
if (b) return std::optional{std::pair{a.value(), b.value()}};
}
return decltype(input2()){};
}
void reqNat() {
std::cout << "Operands must be natural numbers!";
}
void binary(auto f, char op) {
auto xy = input2();
if (xy) {
auto &[x, y] = xy.value();
std::cout << std::format("{} {} {} = {}", x, op, y, f(x, y));
}
else reqNat();
}
are x
and y
subobjects of xy
? If so, is the const
in the return type of input2
required to ensure it? What does the const
change? Would it be good to put it on input
too?
are
x
andy
subobjects ofxy
?
Yes, in:
auto &[x, y] = xy.value();
... xy.value()
returns an lvalue reference to the object inside, and the reference on the left hand side binds to it. x
and y
are then going to be lvalues which name subobjects inside of xy
.
If so, is the
const
in the return type ofinput2
required to ensure it?
No, and const
has nothing to do with this. In:
const auto input2() { /* ... */ }
// ...
auto xy = input2();
// note: this assertion would pass, because xy is not const
static_assert(!std::is_const_v<decltype(xy)>);
... the const
is harmful, because we are declaring an object with auto
, not const auto
. This means we are unnecessarily calling the copy constructor of std::optional<std::pair<...>>
to turn the returned const
object into a non-const
object.
In general, it is not recommended to put const
on return types, because:
What does the
const
change? Would it be good to put it oninput
too?
It only makes us call the copy constructor unnecessarily.
The following use of structured bindings works regardless of whether input2()
returns a const
or non-const
type.
We should not put on input
, we should remove const
from input2
.
Note that there are quite a few things in your code that aren't "good modern C++":
// Constructing a std::set just to check if an int is one of two values is excessive.
std::set<int>{EOF, '\n'}.contains(std::cin.get())
// Consider using something more lightweight, like an IILE
[c = std::cin.get()] { return c == EOF || c == '\n' }()
// alternatively, make the set 'static const' at least
// Excessive use of deduced return types.
// The function name and parameters don't tell us anything about the return
// type being std::optional<long long>.
// Also, unnecessary use of function templates.
// You could have just accepted a std::string_view parameter.
auto input(auto p) { /* ... */ }
// If you have to do this, just use an explicit return type.
// This line is very surprising and could have been return std::nullopt; with
// an explicit type.
return decltype(input2()){};