Date: Fri, 7 Feb 2025 04:31:50 +0000
On 03/02/2025 12:33, Giuseppe D'Angelo via Std-Proposals wrote:
> Hello,
>
> On 03/02/2025 20:41, Marc Edouard Gauthier via Std-Proposals wrote:
> > The simplest fix appears to change this line of the standard:
> >
> > https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412c
> > 210a1893ce/source/utilities.tex#L3538
> > <https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412
> > c210a1893ce/source/utilities.tex#L3538>
> >
> > from:
> >
> > !is_convertible_v<U, T>
> >
> > to :
> >
> > !is_convertible_v<U, T> || is_convertible_v<U, optional<T>>
> >
> > Doing the associated edit on clang/llvm and glibc implementations of
> > std::optional appears to fix the issue.
> >
> > As a quick way to run a test suite, a similar edit of the TartanLLama
> > implementation of optional https://github.com/TartanLlama/optional
> > <https://github.com/TartanLlama/optional>
> >
> > also works, and passes its test suite.
>
> I'm not sure that this actually works, and it's not IFNDR template magic instead. In particular I'm uncomfortable with reading the value of `is_convertible_v<U, optional<T>>`, since that depends from the very constructor / explicit(bool) specifier in which you ask if that trait is true...
Although not a proof, it certainly works fine with clang and GCC. I assume you mean "works" in a more general sense.
Perhaps these existing compilers work by simply taking care to avoid infinite recursion, and not considering a type actively being analyzed by necessity rather than standard mandate.
is_convertible<From, To> is defined in terms of example return code which seems to only require implicit conversion.
The similar convertible_to concept is defined differently and seemingly more strictly, requiring both implicit and explicit conversions, which would avoid any recursion (the optional(U&& v) constructor is only defined as implicit or explicit according to the explicit specifier expression, so can't be both):
>> 18.4.4 Concept convertible_to [concept.convertible]
>>
>> Given types From and To and an expression E whose type and value category are the same as those of
>> declval<From>(), convertible_to<From, To> requires E to be both implicitly and explicitly convertible
>> to type To. The implicit and explicit conversions are required to produce equal results.
I don't know whether it's at all a comparable situation, but optional's assignment operator similarly refers to is_assignable_v :
https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412c210a1893ce/source/utilities.tex#L3800-L3803
https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412c210a1893ce/source/utilities.tex#L3855-L3858
> Maybe you could ask if U has a converting operator towards optional<T> instead. See for instance https://eel.is/c++draft/string.view#cons-12.5
for a precedent.
Sorry I'm missing some subtlety here, I don't understand the difference. Isn't that exactly what is_convertible_v does?
(Also, did you mean 12.4 instead of 12.5? The latter seems unrelated.)
> > Is submitting a defect report appropriate? How might we proceed?
>
> This sounds borderline evolutionary. Maybe submit a paper to LEWG and they can forward it straight to LWG if they think it's a defect?
Writing a paper does seem a big hammer for what seems like a trivial change. I guess I can consider that if this cannot be addressed in a more straightforward manner.
> (It would help if you add strong motivation on why you think it's a defect and not a new feature.)
Basically when considering the optional(U&& v) constructor, if U already converts to optional<T>, making the constructor implicit is a redundant such conversion. And that redundant conversion always ends up ambiguous. Instead of achieving its goal of allowing the conversion, the constructor effectively prevents it.
Reading through several optional related papers, it seems pretty clear this behavior was never the intent, and that it simply hadn't come up in the various scenarios examined.
Fixing this also should not break any existing code, because any existing code that tries this fails to compile. It essentially only allows compiling code that could not previously compile. In that sense it's backward compatible.
In terms of more general motivation, one use case for this is allowing templated code to return some default value as a function of the return type, without knowing the return type. Which happens to help better support an explicit error handling coding style (as opposed to using exceptions) with reduced noise.
> My 2 c,
> --
> Giuseppe D'Angelo
Thank you for the response.
Regards,
Marc
> Hello,
>
> On 03/02/2025 20:41, Marc Edouard Gauthier via Std-Proposals wrote:
> > The simplest fix appears to change this line of the standard:
> >
> > https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412c
> > 210a1893ce/source/utilities.tex#L3538
> > <https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412
> > c210a1893ce/source/utilities.tex#L3538>
> >
> > from:
> >
> > !is_convertible_v<U, T>
> >
> > to :
> >
> > !is_convertible_v<U, T> || is_convertible_v<U, optional<T>>
> >
> > Doing the associated edit on clang/llvm and glibc implementations of
> > std::optional appears to fix the issue.
> >
> > As a quick way to run a test suite, a similar edit of the TartanLLama
> > implementation of optional https://github.com/TartanLlama/optional
> > <https://github.com/TartanLlama/optional>
> >
> > also works, and passes its test suite.
>
> I'm not sure that this actually works, and it's not IFNDR template magic instead. In particular I'm uncomfortable with reading the value of `is_convertible_v<U, optional<T>>`, since that depends from the very constructor / explicit(bool) specifier in which you ask if that trait is true...
Although not a proof, it certainly works fine with clang and GCC. I assume you mean "works" in a more general sense.
Perhaps these existing compilers work by simply taking care to avoid infinite recursion, and not considering a type actively being analyzed by necessity rather than standard mandate.
is_convertible<From, To> is defined in terms of example return code which seems to only require implicit conversion.
The similar convertible_to concept is defined differently and seemingly more strictly, requiring both implicit and explicit conversions, which would avoid any recursion (the optional(U&& v) constructor is only defined as implicit or explicit according to the explicit specifier expression, so can't be both):
>> 18.4.4 Concept convertible_to [concept.convertible]
>>
>> Given types From and To and an expression E whose type and value category are the same as those of
>> declval<From>(), convertible_to<From, To> requires E to be both implicitly and explicitly convertible
>> to type To. The implicit and explicit conversions are required to produce equal results.
I don't know whether it's at all a comparable situation, but optional's assignment operator similarly refers to is_assignable_v :
https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412c210a1893ce/source/utilities.tex#L3800-L3803
https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412c210a1893ce/source/utilities.tex#L3855-L3858
> Maybe you could ask if U has a converting operator towards optional<T> instead. See for instance https://eel.is/c++draft/string.view#cons-12.5
for a precedent.
Sorry I'm missing some subtlety here, I don't understand the difference. Isn't that exactly what is_convertible_v does?
(Also, did you mean 12.4 instead of 12.5? The latter seems unrelated.)
> > Is submitting a defect report appropriate? How might we proceed?
>
> This sounds borderline evolutionary. Maybe submit a paper to LEWG and they can forward it straight to LWG if they think it's a defect?
Writing a paper does seem a big hammer for what seems like a trivial change. I guess I can consider that if this cannot be addressed in a more straightforward manner.
> (It would help if you add strong motivation on why you think it's a defect and not a new feature.)
Basically when considering the optional(U&& v) constructor, if U already converts to optional<T>, making the constructor implicit is a redundant such conversion. And that redundant conversion always ends up ambiguous. Instead of achieving its goal of allowing the conversion, the constructor effectively prevents it.
Reading through several optional related papers, it seems pretty clear this behavior was never the intent, and that it simply hadn't come up in the various scenarios examined.
Fixing this also should not break any existing code, because any existing code that tries this fails to compile. It essentially only allows compiling code that could not previously compile. In that sense it's backward compatible.
In terms of more general motivation, one use case for this is allowing templated code to return some default value as a function of the return type, without knowing the return type. Which happens to help better support an explicit error handling coding style (as opposed to using exceptions) with reduced noise.
> My 2 c,
> --
> Giuseppe D'Angelo
Thank you for the response.
Regards,
Marc
Received on 2025-02-07 04:32:01