When using just pointers/references to std::variant<T...>
defined from T...
where some of T is only forward declared – I have this problem that I cannot even use pointers nor references to this std::variant
– because its bases classes wanted to be instantiated – and they require std::is_default_constructible
and other traits.
See example code:
#include <variant>
struct A;
struct B;
using AB = std::variant<A,B>;
AB* ab();
template <typename T>
void usePointer(T*){}
int main() {
// fails to compile because std::variant gets instantiated here!
usePointer(ab());
}
To simplify the example – this is what happens as I see:
struct A;
struct B;
template <typename T>
void usePointer(T*){}
template <bool> class SomeBase {};
template <typename T>
struct Some : SomeBase<std::is_default_constructible_v<T>>
{};
Some<A>* someA();
int main() {
// this will not compile - because this
// SomeBase<std::is_default_constructible_v<T>>
// does not compile - because A is unknown
usePointer(someA());
}
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/type_traits: In instantiation of 'constexpr const bool std::is_default_constructible_v<A>':
<source>:12:29: required from 'struct Some<A>'
12 | struct Some : SomeBase<std::is_default_constructible_v<T>>
| ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:21:23: required from here
21 | usePointer(someA());
| ^
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/type_traits:3385:54: error: invalid use of incomplete type 'struct A'
3385 | inline constexpr bool is_default_constructible_v = __is_constructible(_Tp);
| ^~~~~~~~~~~~~~~~~~~~~~~
<source>:3:8: note: forward declaration of 'struct A'
3 | struct A;
| ^
Compiler returned: 1
All 3 compilers (gcc,clang,msvc) behave/reject same way – see: https://godbolt.org/z/5cjf1Pv4d
The question: is this correct? For me this is really a bug – either in the compilers implementations or in the standard – however I cannot find anything in the standard mandating this behavior.
“Funny” thing is that when the class template is also forward declared – everything works. I mean – this code is accepted by all compilers:
struct A;
template <typename T>
void usePointer(T*){}
template <typename T> struct Some;
Some<A>* someA();
int main() {
usePointer(someA());
}