C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Making the converting constructor of std::optional less greedy

From: Ville Voutilainen <ville.voutilainen_at_[hidden]>
Date: Thu, 7 Dec 2023 16:46:21 +0200
On Thu, 7 Dec 2023 at 07:15, Egor via Std-Proposals
<std-proposals_at_[hidden]> 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>`.
> }
>
> 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>.

It was an intentional design decision to give the T in optional<T> a
chance to convert directly
from any U, including U=optional<Z>, if it can, instead of wrapping
into an optional<Z> first
and then converting T from Z.

You may think that makes optional not play nice with templated
conversion operators, but
if we make the change you suggest, then someone will come in and say
that optional doesn't
play nice with templated conversion constructors.

Received on 2023-12-07 14:46:35