Date: Fri, 15 Dec 2023 11:36:31 -0500
On Fri, Dec 15, 2023 at 1:11 AM Brian Bi <bbi5291_at_[hidden]> wrote:
>
>
>
> On Thu, Dec 14, 2023, 9:39 PM Jason McKesson via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>
>> On Thu, Dec 14, 2023 at 8:03 PM Brian Bi via Std-Proposals
>> <std-proposals_at_[hidden]> wrote:
>> >
>> > Under the current specification of `std::nullopt_t`, it is unspecified whether certain attempts to construct `std::optional<T>` from nested braced lists are well-formed, for example:
>> >
>> > #include <optional>
>> > struct S1 {
>> > S1() = default;
>> > };
>> > struct S2 {
>> > S2(S1) {}
>> > };
>> > std::optional<S2> os{{{}}};
>> >
>> > With libstdc++, this actually selects the `std::nullopt_t` overload of the `std::optional<S2>` constructor, which is then ill-formed because the constructor of `std::nullopt_t` is explicit.
>>
>> That a bug in libstdc++. Clang and MSVC don't have a problem:
>> https://gcc.godbolt.org/z/Ta1PGzhhT
>>
>> The C++20 standard states that "Type nullopt_t shall not have a
>> default constructor or an initializer-list constructor, and shall not
>> be an aggregate." That means that no amount of curly braces should be
>> able to create one. It's likely that they didn't quite define
>> `nullopt_t` correctly; pre-C++20 aggregate changes, you have to do
>> quite a lot of defensive coding to meet those requirements.
>
>
> List-initialization can call a non-initializer-list constructor, so nothing in the wording implies that libstdc++ is non-conforming. If you look at libstdc++'s definition of `std::nullopt_t` you'll see why it behaves the way it does.
Yeah, and it's buggy.
According to [dcl.init.list/3], list-initialization syntax with an
empty list can do exactly and only the following:
1. Call a default constructor.
2. Call an initializer-list constructor with an empty list.
3. Invoke aggregate initialization with an empty sequence of elements.
I found this site with some version of libstdc++'s implementation:
https://gcc.gnu.org/onlinedocs/gcc-5.4.0/libstdc++/api/a01424_source.html
That implementation is:
```
struct nullopt_t
{
// Do not user-declare default constructor at all for
// optional_value = {} syntax to work.
// nullopt_t() = delete;
// Used for constructing nullopt.
enum class _Construct { _Token };
// Must be constexpr for nullopt_t to be literal.
explicit constexpr nullopt_t(_Construct) { }
};
```
The standard explicitly says that `nullopt_t` "shall not be an
aggregate". This class is an aggregate. This class definition violates
the standard.
QED.
>
>
>
> On Thu, Dec 14, 2023, 9:39 PM Jason McKesson via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>
>> On Thu, Dec 14, 2023 at 8:03 PM Brian Bi via Std-Proposals
>> <std-proposals_at_[hidden]> wrote:
>> >
>> > Under the current specification of `std::nullopt_t`, it is unspecified whether certain attempts to construct `std::optional<T>` from nested braced lists are well-formed, for example:
>> >
>> > #include <optional>
>> > struct S1 {
>> > S1() = default;
>> > };
>> > struct S2 {
>> > S2(S1) {}
>> > };
>> > std::optional<S2> os{{{}}};
>> >
>> > With libstdc++, this actually selects the `std::nullopt_t` overload of the `std::optional<S2>` constructor, which is then ill-formed because the constructor of `std::nullopt_t` is explicit.
>>
>> That a bug in libstdc++. Clang and MSVC don't have a problem:
>> https://gcc.godbolt.org/z/Ta1PGzhhT
>>
>> The C++20 standard states that "Type nullopt_t shall not have a
>> default constructor or an initializer-list constructor, and shall not
>> be an aggregate." That means that no amount of curly braces should be
>> able to create one. It's likely that they didn't quite define
>> `nullopt_t` correctly; pre-C++20 aggregate changes, you have to do
>> quite a lot of defensive coding to meet those requirements.
>
>
> List-initialization can call a non-initializer-list constructor, so nothing in the wording implies that libstdc++ is non-conforming. If you look at libstdc++'s definition of `std::nullopt_t` you'll see why it behaves the way it does.
Yeah, and it's buggy.
According to [dcl.init.list/3], list-initialization syntax with an
empty list can do exactly and only the following:
1. Call a default constructor.
2. Call an initializer-list constructor with an empty list.
3. Invoke aggregate initialization with an empty sequence of elements.
I found this site with some version of libstdc++'s implementation:
https://gcc.gnu.org/onlinedocs/gcc-5.4.0/libstdc++/api/a01424_source.html
That implementation is:
```
struct nullopt_t
{
// Do not user-declare default constructor at all for
// optional_value = {} syntax to work.
// nullopt_t() = delete;
// Used for constructing nullopt.
enum class _Construct { _Token };
// Must be constexpr for nullopt_t to be literal.
explicit constexpr nullopt_t(_Construct) { }
};
```
The standard explicitly says that `nullopt_t` "shall not be an
aggregate". This class is an aggregate. This class definition violates
the standard.
QED.
Received on 2023-12-15 16:36:38