Date: Mon, 3 Feb 2025 19:41:18 +0000
We've encountered a limitation of the C++ library that doesn't seem readily fixable without modifying the library, in turn requiring a corresponding small fix to the C++ standard.
Does the following look right?
For example, the following https://godbolt.org/z/f45bPd4sz
fails to compile due to an ambiguous conversion:
// Implicitly auto-converts to anything.
static const struct default_value_t {
template <class T> operator T() const { return T {}; }
} default_value {};
int main() {
std::optional<int> a = default_value; // fails to compile
return 0;
}
GCC 14.2 output for example (similar on clang):
<source>: In function 'int main()':
<source>:13:28: error: conversion from 'const default_value_t' to 'std::optional<int>' is ambiguous
13 | std::optional<int> a = default_value;
| ^~~~~~~~~~~~~
<source>:7:5: note: candidate: 'default_value_t::operator T() const [with T = std::optional<int>]'
7 | operator T() const
| ^~~~~~~~
In file included from <source>:1:
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/optional:745:9: note: candidate: 'constexpr std::optional<_Tp>::optional(_Up&&) [with _Up = const default_value_t&; typename std::enable_if<__and_v<std::__not_<std::is_same<std::optional<_Tp>, typename std::remove_cv<typename std::remove_reference<_Up>::type>::type> >, std::__not_<std::is_same<std::in_place_t, typename std::remove_cv<typename std::remove_reference<_Up>::type>::type> >, std::is_constructible<_Tp, _Up>, std::is_convertible<_Up, _Tp> >, bool>::type <anonymous> = true; _Tp = int]'
745 | optional(_Up&& __t)
| ^~~~~~~~
The simplest fix appears to change this line of the standard:
https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412c210a1893ce/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
also works, and passes its test suite. Specifically, the change there was this:
/// Constructs the stored value with `u`.
template <
class U = T,
- detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::enable_if_t<std::is_convertible<U &&, T>::value &&
+ !std::is_convertible_v<U &&, optional<T>>> * = nullptr,
detail::enable_forward_value<T, U> * = nullptr>
constexpr optional(U &&u) : base(in_place, std::forward<U>(u)) {}
template <
class U = T,
- detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::enable_if_t<!std::is_convertible<U &&, T>::value ||
+ std::is_convertible_v<U &&, optional<T>>> * = nullptr,
detail::enable_forward_value<T, U> * = nullptr>
constexpr explicit optional(U &&u) : base(in_place, std::forward<U>(u)) {}
Is submitting a defect report appropriate? How might we proceed?
Best regards,
Marc
PS. A similar limitation may exist elsewhere in the standard. For example, std::unique_ptr appears to have the same issue (while std::shared_ptr does not) on versions of clang and gcc on-hand. This was not investigated.
Does the following look right?
For example, the following https://godbolt.org/z/f45bPd4sz
fails to compile due to an ambiguous conversion:
// Implicitly auto-converts to anything.
static const struct default_value_t {
template <class T> operator T() const { return T {}; }
} default_value {};
int main() {
std::optional<int> a = default_value; // fails to compile
return 0;
}
GCC 14.2 output for example (similar on clang):
<source>: In function 'int main()':
<source>:13:28: error: conversion from 'const default_value_t' to 'std::optional<int>' is ambiguous
13 | std::optional<int> a = default_value;
| ^~~~~~~~~~~~~
<source>:7:5: note: candidate: 'default_value_t::operator T() const [with T = std::optional<int>]'
7 | operator T() const
| ^~~~~~~~
In file included from <source>:1:
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/optional:745:9: note: candidate: 'constexpr std::optional<_Tp>::optional(_Up&&) [with _Up = const default_value_t&; typename std::enable_if<__and_v<std::__not_<std::is_same<std::optional<_Tp>, typename std::remove_cv<typename std::remove_reference<_Up>::type>::type> >, std::__not_<std::is_same<std::in_place_t, typename std::remove_cv<typename std::remove_reference<_Up>::type>::type> >, std::is_constructible<_Tp, _Up>, std::is_convertible<_Up, _Tp> >, bool>::type <anonymous> = true; _Tp = int]'
745 | optional(_Up&& __t)
| ^~~~~~~~
The simplest fix appears to change this line of the standard:
https://github.com/cplusplus/draft/blob/5bfd514aa5d9a19d1dd0f4e874412c210a1893ce/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
also works, and passes its test suite. Specifically, the change there was this:
/// Constructs the stored value with `u`.
template <
class U = T,
- detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::enable_if_t<std::is_convertible<U &&, T>::value &&
+ !std::is_convertible_v<U &&, optional<T>>> * = nullptr,
detail::enable_forward_value<T, U> * = nullptr>
constexpr optional(U &&u) : base(in_place, std::forward<U>(u)) {}
template <
class U = T,
- detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr,
+ detail::enable_if_t<!std::is_convertible<U &&, T>::value ||
+ std::is_convertible_v<U &&, optional<T>>> * = nullptr,
detail::enable_forward_value<T, U> * = nullptr>
constexpr explicit optional(U &&u) : base(in_place, std::forward<U>(u)) {}
Is submitting a defect report appropriate? How might we proceed?
Best regards,
Marc
PS. A similar limitation may exist elsewhere in the standard. For example, std::unique_ptr appears to have the same issue (while std::shared_ptr does not) on versions of clang and gcc on-hand. This was not investigated.
Received on 2025-02-03 19:41:23