3

I need to specify different ranges::views depending upon a variable. Such as:

#include <ranges>
#include <vector>

auto main() -> int
{
    std::vector<double> xs = { 1.0, 2.0, 3.0, 4.0, 5.0 };
    auto x = int(0);

    auto vi;

    if(x == 0)
        vi = xs | std::ranges::views::all;
    else
        vi = xs | std::ranges::views::drop(2); 
}

but the problem is auto can't determine a type so I need to define it as I've defined the int type for x. (i.e., auto x = int(0);).

How do I do this for a view?

3
  • @frank Yes, I'm aware of that. I use this convention as I find it more intuitive. However, I don't know what type ranges::views would be hence my question. Do you know by any chance? Commented Jun 17, 2021 at 22:26
  • I think the best you can do would be std::variant<decltype(xs | std::ranges::views::all), decltype(xs | std::ranges::views::drop(2))> vi;, but I would personally rather revisit the design in general instead of going down that rabbit hole. Commented Jun 17, 2021 at 22:26
  • @frank I see. There are terms there that I'm not familiar with (although I get the gist). I need to have a read up about variant and decltype. Commented Jun 17, 2021 at 22:32

1 Answer 1

6

Normally, you would want to avoid this altogether by not declaring vi until it can be inferred properly, which is not as constraining as you might think:

#include <ranges>
#include <vector>
#include <iostream>

template<typename RangeT>
void foo(const RangeT& vi) {
    for(auto & v: vi) {
        std::cout << v << "\n";
    }
}

auto main() -> int
{
    std::vector<double> xs = { 1.0, 2.0, 3.0, 4.0, 5.0 };
    auto x = int(0);

    if(x == 0) {
        auto vi = xs | std::ranges::views::all;
        foo(vi);
    }
    else {
        auto vi = xs | std::ranges::views::drop(2); 
        foo(vi);
    }
}

That being said, in case anyone lands here and has really painted themselves in a corner, it's still worth answering the question as asked.

You can use decltype() to identify the types that will be used, and then use std::variant<> to make vi a type that can hold either of them:

#include <ranges>
#include <vector>
#include <variant>
#include <iostream>

auto main() -> int
{
    std::vector<double> xs = { 1.0, 2.0, 3.0, 4.0, 5.0 };
    auto x = int(0);

    using vi_all = decltype(xs | std::ranges::views::all);
    using vi_drop_2 = decltype(xs | std::ranges::views::drop(2));
    std::variant<vi_all, vi_drop_2> vi;

    if(x == 0)
        vi = xs | std::ranges::views::all;
    else
        vi = xs | std::ranges::views::drop(2); 


    std::visit([](auto& vi){
        for(auto& v: vi) {
            std::cout << v << "\n";
        }
    }, vi);
}
Sign up to request clarification or add additional context in comments.

1 Comment

In the latter case, you might as well do auto process_view = [] ...; followed by if (x == 0) process_view(...); else process_view(...);. Shorter. (Though worth noticing is that in general vi_all and vi_drop_2 are different types, so you cannot get away without variant/equivalent.)

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.