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