Given the following code:
#include <iostream>
#include <optional>
struct foo
{
explicit operator std::optional<int>() {
return std::optional<int>( 1 );
}
explicit operator int() {
return 0;
}
};
int main()
{
foo my_foo;
std::optional<int> my_opt( my_foo );
std::cout << "value: " << my_opt.value() << std::endl;
}
gcc 7.2.0 writes value: 1
.
MSVC 2017 (15.3) and clang 4.0.0 however write value: 0
.
Which one is correct according to the C++ standard?
Since this is direct-initialization, we enumerate the constructors and just pick the best one. The relevant constructors for std::optional
are :
constexpr optional( const optional& other ); // (2)
constexpr optional( optional&& other ) noexcept(/* see below */); // (3)
template < class U = value_type >
/* EXPLICIT */ constexpr optional( U&& value ); // (8), with U = foo&
Both are viable ((8)
only participates in overload resolution if int
is constructible from foo&
and foo
is neither std::in_place_t
nor std::optional<int>
, all of which hold), but (8)
is an exact match whereas (2)
and (3)
require a user-defined conversion, so it should be preferred. gcc is wrong here.
However, gcc doesn't actually invoke (3)
either. It just directly initializes my_opt
from the result of converting my_foo
to an optional<int>
. This program with gcc 7.2 prints 3
but none of 1a
, 1b
, or 2
:
#include <iostream>
template <class T>
struct opt {
opt() { }
opt(opt const& ) { std::cout << "1a\n"; }
opt(opt&& ) { std::cout << "1b\n"; }
template <class U>
opt(U&& ) { std::cout << "2\n"; }
};
struct foo
{
explicit operator opt<int>() { std::cout << "3\n"; return {}; }
};
int main()
{
opt<int> o(foo{});
}
I don't think that's an allowable route. I filed 81952.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With