I'm poking around in the myst of C++ instantiation / declaration order. Here's a fun bit I came across:
This compiles :
#include <cstddef>
#include <variant>
#include <array>
template <size_t V>
struct container
{
// THIS COMPILES
struct array;
using val = std::variant<std::monostate, int, array>;
// THIS DOESNT
// using val = std::variant<std::monostate, int, struct array>;
struct proxy : val
{
using val::variant;
};
struct array { };
};
int main()
{
container<10> ctr;
}
But when you opt for in-place declarations, it suddenly stops working (Demo):
#include <cstddef>
#include <variant>
#include <array>
template <size_t V>
struct container
{
// THIS COMPILES
// struct array;
// using val = std::variant<std::monostate, int, array>;
// THIS DOESNT
using val = std::variant<std::monostate, int, struct array>;
struct proxy : val
{
using val::variant;
};
struct array { };
};
int main()
{
container<10> ctr;
}
This is the error I get:
/opt/compiler-explorer/gcc-trunk-20220729/include/c++/13.0.0/type_traits:1012:52: error: static assertion failed: template argument must be a complete class or an unbounded array
1012 | static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
|
Can someone explain me exactly why this happens? What is the difference?
EDIT: You are allowed in certain circumstances to declare a type in a template argument list:
#include <cstddef>
#include <variant>
#include <array>
#include <cstdio>
void foo(std::initializer_list<struct array>);
struct array
{
array(int a) : a_{a} {}
void print() {
printf("%d\n", a_);
}
int a_;
};
void foo(std::initializer_list<struct array> init) {
for (auto a : init) {
a.print();
}
printf(".. it works\n");
}
int main()
{
foo({1,2,3});
}
I don't know when and where this applies though.