On Thu, 7 Dec 2023, 05:15 Egor via Std-Proposals, <std-proposals@lists.isocpp.org> wrote:
Hello!

std::optional<T> has a really greedy converting constructor. It accepts
almost any type that T is constructible from, and constructs a T from
it, even if that type is also convertible to optional<T>. Example:
https://gcc.godbolt.org/z/ncsYehW19

     #include <iostream>
     #include <optional>
     #include <source_location>

     struct A
     {
         template <typename T>
         operator T()
         {
             std::cout <<
std::source_location::current().function_name() << '\n';
             return {};
         }
     };

     int main()
     {
         std::optional<int> x(A{}); // (1) Calls `operator int`, not
`operator optional<int>`.
         // std::optional<int> y = A{}; // (2) Ambiguous.
         x = A{}; // (3) Calls `operator int`, not `operator optional<int>`.
     }


What's the practical use case for this type's conversion operator? If it can convert to both int and optional int, what does that mean? Why is it a problem to convert to int first and then wrap that in an optional? Does your type have a special way to construct an optional that is more efficient than converting to an int and then setting optional<int> from that?

"I find this confusing" is not very strong motivation.


This is very confusing in my eyes. std::optional is first type I see in
the wild that doesn't play nice with templated conversion operators.

I suggest we make this constructor of optional not participate in
overload resolution if the argument is convertible to optional<T>.

This would make (2) valid, and make all three lines call A::operator
optional<T>.

--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals