The following program
#include <optional>
#include <iostream>
int main()
{
std::optional<int> a;
constexpr bool x = true;
const std::optional<int> & b = x ? a : std::nullopt;
std::cout << (&a == &b);
const std::optional<int> & c = x ? a : (const std::optional<int> &)std::nullopt;
std::cout << (&a == &c);
const std::optional<int> & d = x ? a : (const std::optional<int>)std::nullopt;
std::cout << (&a == &d);
}
prints 010
even in case of compiler optimization:
https://gcc.godbolt.org/z/asTrzdE3c
Could you please explain why it is so, and what is the difference between seemingly identical 3 cases?
First you have to undesund what is the type of ternary operator result.
x ? a : std::nullopt;
Here a
is variable which can be reference and std::nullopt
is something which is implicitly converted to optional of matching type (here std::optional<int>
).
So conversion of std::nullopt
ends with creation of temporary value. To match type a
is also copied.
So ternary operator deduces type to be a value of type std::optional<int>
which becomes a temporary object. New instance of std::optional<int>
is created.
Now const auto &
is able to prolong lifetime of temporaries. So b
is reference to std::optional<int>
which is a temporary of prolonged lifetime.
d
is same scenario, but it is more explicit.
c
has (const std::optional<int> &)std::nullopt
which is creates temporary object of std::optional<int>
with prolonged lifetiem. Here ternary operator has as argument something what is std::optional<int>&
and const std::optional<int>&
so it is able to pass first arguments reference return type.
See what cppinsights generates with your code:
#include <optional>
#include <iostream>
int main()
{
std::optional<int> a = std::optional<int>();
constexpr const bool x = true;
const std::optional<int> & b = x ? std::optional<int>(a) : std::optional<int>(std::nullopt_t(std::nullopt));
std::cout.operator<<((&a == &b));
const std::optional<int> & c = x ? a : static_cast<const std::optional<int>>(std::optional<int>(std::nullopt_t(std::nullopt)));
std::cout.operator<<((&a == &c));
const std::optional<int> & d = (x ? std::optional<int>(a) : static_cast<const std::optional<int>>(std::optional<int>(std::nullopt_t(std::nullopt))));
std::cout.operator<<((&a == &d));
}