1

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.

5
  • 4
    Reduced. Commented May 14 at 8:12
  • On gcc problem appears since gcc 15: reduced code example (full code demo). Commented May 14 at 9:05
  • @康桓瑋 This directly reduces to the age-old incomplete type in variant. LB-: look at this answer: stackoverflow.com/questions/57226629/… . Commented May 14 at 11:49
  • Ah, so it's actually std::variant being 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 have get_sub_view return a range-like object that delays the template expansion enough via templated member functions? Commented May 14 at 17:31
  • @LB-- You just need to delay the instantiation of the std::variant template until all 3 structs are complete. For example you could declare get_sub_view as returning a placeholder type (auto) and only define it after all 3 structs are complete - Example: godbolt. Commented May 14 at 18:45

1 Answer 1

0

With some hints from the comments on my question, I figured out an embarrassingly simple workaround: change the TransformView type alias into a struct or class instead so it can be forward-declared, and then just inherit from std::ranges::transform_view.

struct VA;
struct VB;
struct VC;

struct TansformView;

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;
};

using InputView = std::ranges::ref_view<std::span<Input const> const>;
struct TansformView
: std::ranges::transform_view<InputView, TransformerFunctor<VA, VB, VC>>
{
    using transform_view::transform_view;
};

bool demo(std::span<Input const> const inputs)
{
    //...

This way, the TansformView get_sub_view() const; line can remain as-is, no need to convert it to a template to delay instantiation, and the actual definition of TansformView can come after all the other necessary types are complete.

Demo: https://compiler-explorer.com/z/7bhzcPb1r

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.