Home > front end >  Why can't ranges be used if in a function?
Why can't ranges be used if in a function?

Time:04-23

I'm trying to get a range like python like below:

#include <iostream>
#include <ranges>

auto Range(double start, double end, double step)
{
    if (start <= end) {
        auto step_fun = [=](auto x) { return x * step   start; };
        auto end_pred = [=](auto x) { return x <= end; };
        auto range =
            std::views::iota(0)
            | std::views::transform(step_fun)
            | std::views::take_while(end_pred);
        return range;
    }
    else {
        auto step_fun = [=](auto x) { return -x * step   start; };
        auto end_pred = [=](auto x) { return x >= end; };
        auto range =
            std::views::iota(0)
            | std::views::transform(step_fun)
            | std::views::take_while(end_pred);
        return range;
    }
}
int main() {
    auto range = Range(108, 100, 1);

    for (auto i : range)
        std::cout << i << ' ';
    return 0;
}

But the Visual Studio told me:

Error   C3487   'std::ranges::take_while_view<std::ranges::transform_view<std::ranges::iota_view<_Ty,std::unreachable_sentinel_t>,Range::<lambda_3>>,Range::<lambda_4>>': all return expressions must deduce to the same type: previously it was 'std::ranges::take_while_view<std::ranges::transform_view<std::ranges::iota_view<_Ty,std::unreachable_sentinel_t>,Range::<lambda_1>>,Range::<lambda_2>>'   

and

Message     No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called    test    

But only start>endorstart<end,it can work,like:

auto Range(double start, double end, double step)
{
auto step_fun = [=](auto x) { return x * step   start; };
        auto end_pred = [=](auto x) { return x <= end; };
        auto range =
            std::views::iota(0)
            | std::views::transform(step_fun)
            | std::views::take_while(end_pred);
        return range;
}

How can I fix it?

CodePudding user response:

A function must have one return type, auto doesn't change that. Your two returns have different, incompatible types, because each lambda expression has a unique type.

The error message rather buried it: blah blah Range::<lambda_3>,Range::<lambda_4> vs blah blah Range::<lambda_1>,Range::<lambda_2>

You can do some arithmetic to reduce it to one return

auto Range(double start, double end, double step)
{
    if (start > end) {
        step *= -1;
    }
    int count = (end - start) / step;

    auto step_fun = [=](auto x) { return x * step   start; };
    return std::views::iota(0, count)
         | std::views::transform(step_fun);
}

Caveat: this function is still problematic if step is negative, you might instead want to throw if end is not reachable from start.

CodePudding user response:

Because the result type of take_while and transform contains the function type as part of signature, but one function can only have single return type.

in addition to what already answered, you can also erase the type.

#include <iostream>
#include <ranges>
#include <functional>

auto Range(double start, double end, double step)
{
    std::function<double(double)> step_fun;
    std::function<double(double)> end_pred;

    if (start <= end) {
        step_fun = [=](auto x) { return x * step   start; };
        end_pred = [=](auto x) { return x <= end; };
    }
    else {
        step_fun = [=](auto x) { return -x * step   start; };
        end_pred = [=](auto x) { return x >= end; };    
    }
    auto range =
            std::views::iota(0)
            | std::views::transform(step_fun)
            | std::views::take_while(end_pred);
    return range;
}
int main() {
    auto range = Range(108, 100, 1);

    for (auto i : range)
        std::cout << i << ' ';
    return 0;
}
  • Related