https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114076

--- Comment #3 from Benjamin Buch <benni.buch at gmail dot com> ---
I [created an overview](https://stackoverflow.com/a/78101462/4821621) with all
cases that currently work on StackOverflow. I think that all these cases should
be valid. For a properly formated version with links to Compiler Explorer
please use my stackoverflow post. Here is the relevant part

GitHub user
[frederick-vs-ja](https://github.com/microsoft/STL/issues/4417#issuecomment-1960976417)
has reduced the problem in the MS STL Bug Report to a minimal example.

```cpp
template <typename T>
struct Holder {
    Holder() = default;
    constexpr ~Holder() { static_assert(sizeof(T) || true); }
};

struct Incomplete;

struct Class {
    Class();
    ~Class();

    Holder<Incomplete> v{};
};

int main() { [[maybe_unused]] Class v; }
```

I have extended this example with some variants and tested the compilers with
them. Syntactically, there are 7 ways to call the default constructor of
Holder:

- `Holder<Incomplete> a;` default-initialization
- `Holder<Incomplete> b{};` direct-list-initialization
- `Holder<Incomplete> c = {};` copy-list-initialization
- `Holder<Incomplete> d = Holder<Incomplete>();` copy-direct-initialization
- `Holder<Incomplete> e = {Holder<Incomplete>()};` copy-list-initialization
- `Holder<Incomplete> f = Holder<Incomplete>{};` copy-direct-initialization
- `Holder<Incomplete> g = {Holder<Incomplete>{}};` copy-list-initialization

Then we can change `Holder` in two ways. (Thanks to Github user
[fsb4000](https://github.com/microsoft/STL/issues/4417#issuecomment-1959604006)
in the MS STL bug report!)

First we can remove the explicit constructor definition, so the compiler
generates this constructor implicitly:

```cpp
template <typename T>
struct Holder {
    constexpr ~Holder() { static_assert(sizeof(T) || true); }
};
```

Second we can remove the constexpr from the destructor:

```cpp
template <typename T>
struct Holder {
    Holder() = default;
    ~Holder() { static_assert(sizeof(T) || true); }
};
```

A third relevant change can be applied to `B`. (Thanks to [Jiang
An](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114076#c1) in the GCC bug
report!) We can make it a template, by giving it a default template argument:

```cpp
template <typename = void>
struct Class {
    Class();
    ~Class();

    Holder<Incomplete> v{};
};
```

So we have 8 code examples with 7 kinds of initialization. I created a table
with what works with which compiler. I tested with MSVC 19.38, GCC 13.2 and
clang 17.0.1. Each table cell links to the live code, where the trunk versions
of the compilers are used additionally.

- Case 1: *Implicit `Holder` ctor*, *non-`constexpr` dtor*, *non-template
`Class`*
- Case 2: Explicit `Holder` ctor, *non-`constexpr` dtor*, *non-template
`Class`*
- Case 3: *Implicit `Holder` ctor*, `constexpr` dtor, *non-template `Class`*
- Case 4: Explicit `Holder` ctor, `constexpr` dtor, *non-template `Class`*
- Case 5: *Implicit `Holder` ctor*, *non-`constexpr` dtor*, template `Class`
- Case 6: Explicit `Holder` ctor, *non-`constexpr` dtor*, template `Class`
- Case 7: *Implicit `Holder` ctor*, `constexpr` dtor, template `Class`
- Case 8: Explicit `Holder` ctor, `constexpr` dtor, template `Class`

The order is: ✅/❌ MSVC ; ✅/❌ GCC ; ✅/❌ clang

||Case a|Case b|Case c|Case d|Case e|Case f|Case g|
|-|-|-|-|-|-|-|-|
|Case 1:|✅✅✅|✅✅✅|✅✅✅|✅❌✅|✅❌✅|✅❌✅|✅❌✅|
|Case 2:|✅✅✅|✅✅✅|✅❌✅|✅❌✅|✅❌✅|✅❌✅|✅❌✅|
|Case 3:|✅✅✅|❌✅✅|❌✅✅|❌❌❌|❌❌❌|❌❌❌|❌❌❌|
|Case 4:|✅✅✅|❌✅✅|❌❌✅|❌❌❌|❌❌❌|❌❌❌|❌❌❌|
|Case 5:|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|
|Case 6:|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|
|Case 7:|✅✅✅|✅✅✅|✅✅✅|✅✅❌|✅✅❌|✅✅❌|✅✅❌|
|Case 8:|✅✅✅|✅✅✅|✅✅✅|✅✅❌|✅✅❌|✅✅❌|✅✅❌|

Cases d, e, f and g can apparently be considered as one case.

Reply via email to