I'm writing a C++23 program and trying to create a recursive data structure with help from std::ranges::transform_view. However, it seems like the requires clauses on the std::ranges::transform_view template result in requiring complete class types too soon, making it impossible to have the recursion. I already have this working by other means, but it would be nice to use the ranges library to simplify this.
Here is a simplified demonstration of the problem:
#include <ranges>
#include <span>
#include <variant>
struct Input
{
int type;
int data;
};
template<typename... V>
struct TransformerFunctor
{
using variant = std::variant<std::monostate, V...>;
[[nodiscard]] /*static*/ constexpr variant operator()(Input const input) const noexcept
{
variant ret(std::in_place_type<std::monostate>);
([&]
{
if(V::TYPE == input.type)
{
ret.template emplace<V>(input.data);
}
}(), ...);
return ret;
}
};
struct VA;
struct VB;
struct VC;
using InputView = std::ranges::ref_view<std::span<Input const> const>;
using TansformView = std::ranges::transform_view<InputView, TransformerFunctor<VA, VB, VC>>;
struct VA
{
static constexpr int TYPE = 1;
VA(int) noexcept;
};
struct VB
{
static constexpr int TYPE = 2;
VB(int) noexcept;
[[nodiscard]] TansformView get_sub_view() const;
};
struct VC
{
static constexpr int TYPE = 3;
VC(int) noexcept;
};
bool demo(std::span<Input const> const inputs)
{
TansformView view(InputView(inputs), {});
for(std::variant const v : view)
{
if(std::holds_alternative<VB>(v))
{
return !std::ranges::empty(std::get<VB>(v).get_sub_view());
}
}
return false;
}
Compiler explorer link: https://compiler-explorer.com/z/4x487ervE
Interestingly, it seems like Clang 20.1.0 accepts this code and compiles it without issue. GCC and MSVC on the other hand both generate compile errors related to the VA, VB, and VC types being incomplete types.
Is this actually supposed to work, and Clang is correct? Or is this supposed to not work and one of the others is correct? And in either case, is there any simple way I can fix this problem?
I already know that converting get_sub_view and using TansformView into templates to delay the template expansion until point of call can work around this issue, but it makes the code more complex and in my actual project I can't inline the definition of that function.
std::variantbeing the issue here? That's good to know I suppose, but it still leaves room for an answer related to this specific use case. Is there some way to haveget_sub_viewreturn a range-like object that delays the template expansion enough via templated member functions?std::varianttemplate until all 3 structs are complete. For example you could declareget_sub_viewas returning a placeholder type (auto) and only define it after all 3 structs are complete - Example: godbolt.