具有自动和结构化绑定的 Ranges V3 zip
Ranges V3 zip with auto and structured bindings
我希望能够使用 C++ 范围通过压缩容器而不是显式索引到它们来帮助简化代码逻辑。我可以让它与冗长的 lambda 参数一起工作,但我宁愿尝试使用更多 auto
.
来使其 simpler/generalizable
const int n = ...;
std::vector<float> a(n), b(n), c(n);
...initialize a and b...
// This works
ranges::for_each(
ranges::views::zip(a, b, c),
[](const std::tuple<float&, float&, float&>& v)
{
const auto& [a, b, c] = v;
c = a + b;
std::cout << typeid(v).name(); // NSt3__15tupleIJRfS1_S1_EEE
}
);
// This fails
ranges::for_each(
ranges::views::zip(a, b, c),
[](const auto& v)
{
const auto& [a, b, c] = v;
// c = a + b;
std::cout << typeid(v).name(); // N6ranges12common_tupleIJRfS1_S1_EEE
}
);
Ranges-v3 文档说明如下:
views::zip
Given N ranges, return a new range where Mth element is the result of calling make_tuple
on the Mth elements of all N ranges.
这让我觉得我应该能够将 ranges::common_tuple
转换为 std::tuple
,我查看了 public 成员并发现:
std::tuple< Ts... > const & base() const noexcept
但是这也不能编译:
const auto& [a, b, c] = v.base();
// error: no member named 'base' in 'std::__1::tuple<float, float, float>'
但是当我打印 typeid(v)
时,它不是 std::tuple
;它是 ranges::common_tuple
。我在这里尝试使用 auto
类型推导可以吗? (clang 编译器,如果重要的话)
简短的回答是:如果您实际上不需要 const
,请不要使用 const
。你想修改一些东西,为什么要const
?这很好用:
ranges::for_each(
ranges::views::zip(a, b, c),
[](auto&& v)
{
auto&& [a, b, c] = v;
c = a + b;
}
);
和较短的一样:
for (auto&& [a, b, c] : ranges::views::zip(a, b, c)) {
c = a + b;
}
你休息的原因有点微妙。基本上,ranges::for_each
受限于 indirectly_unary_invocable
,这需要所有:
invocable<F &, iter_value_t<I> &> &&
invocable<F &, iter_reference_t<I>> &&
invocable<F &, iter_common_reference_t<I>> &&
因此,您的 lambda 将使用所有这三种类型进行实例化。其中一种类型 (iter_value_t<I>&
) 是 tuple<float, float, float>&
。因此,当您使用 const auto&
进行结构化绑定时,每个绑定的类型都是 const float
。这就是为什么它不可分配的原因——但这仅适用于特定的实例化(无论如何都不是在运行时调用的实例化)。
我希望能够使用 C++ 范围通过压缩容器而不是显式索引到它们来帮助简化代码逻辑。我可以让它与冗长的 lambda 参数一起工作,但我宁愿尝试使用更多 auto
.
const int n = ...;
std::vector<float> a(n), b(n), c(n);
...initialize a and b...
// This works
ranges::for_each(
ranges::views::zip(a, b, c),
[](const std::tuple<float&, float&, float&>& v)
{
const auto& [a, b, c] = v;
c = a + b;
std::cout << typeid(v).name(); // NSt3__15tupleIJRfS1_S1_EEE
}
);
// This fails
ranges::for_each(
ranges::views::zip(a, b, c),
[](const auto& v)
{
const auto& [a, b, c] = v;
// c = a + b;
std::cout << typeid(v).name(); // N6ranges12common_tupleIJRfS1_S1_EEE
}
);
Ranges-v3 文档说明如下:
views::zip
Given N ranges, return a new range where Mth element is the result of calling
make_tuple
on the Mth elements of all N ranges.
这让我觉得我应该能够将 ranges::common_tuple
转换为 std::tuple
,我查看了 public 成员并发现:
std::tuple< Ts... > const & base() const noexcept
但是这也不能编译:
const auto& [a, b, c] = v.base();
// error: no member named 'base' in 'std::__1::tuple<float, float, float>'
但是当我打印 typeid(v)
时,它不是 std::tuple
;它是 ranges::common_tuple
。我在这里尝试使用 auto
类型推导可以吗? (clang 编译器,如果重要的话)
简短的回答是:如果您实际上不需要 const
,请不要使用 const
。你想修改一些东西,为什么要const
?这很好用:
ranges::for_each(
ranges::views::zip(a, b, c),
[](auto&& v)
{
auto&& [a, b, c] = v;
c = a + b;
}
);
和较短的一样:
for (auto&& [a, b, c] : ranges::views::zip(a, b, c)) {
c = a + b;
}
你休息的原因有点微妙。基本上,ranges::for_each
受限于 indirectly_unary_invocable
,这需要所有:
invocable<F &, iter_value_t<I> &> &&
invocable<F &, iter_reference_t<I>> &&
invocable<F &, iter_common_reference_t<I>> &&
因此,您的 lambda 将使用所有这三种类型进行实例化。其中一种类型 (iter_value_t<I>&
) 是 tuple<float, float, float>&
。因此,当您使用 const auto&
进行结构化绑定时,每个绑定的类型都是 const float
。这就是为什么它不可分配的原因——但这仅适用于特定的实例化(无论如何都不是在运行时调用的实例化)。