【问题标题】:Using '|' (pipe) operator with std::views does not compile使用“|”带有 std::views 的(管道)运算符无法编译
【发布时间】:2021-12-01 21:53:41
【问题描述】:

在转行之后,我正在努力学习 std::views(以及一般的函数式编程)。我正在使用“|” (管道)运算符与向量上的 std::views::filter ,我很困惑为什么有些代码结构可以编译而其他代码结构不编译。

这段代码创建了一个由int 组成的向量,然后按总和过滤它们。我已经评论了三个让我感到困惑的语句,前两个可以编译,第三个没有。

编译错误为:

'|': no operator found which takes a left-hand operand of type 'std::vector<std::vector<int,std::allocator<int>>,std::allocator<std::vector<int,std::allocator<int>>>>' (or there is no acceptable conversion)  

(使用MSVC19,使用/std:c++latest编译)

我很困惑为什么这不能编译,而 (2) 尤其如此?

#include <vector>
#include <numeric>
#include <ranges>

template<typename T>
auto buildMultiples(const std::vector<T>& base)
{
    std::vector<std::vector<T>> vRet;
    for(T n= 1; n <= 5; n++)
    {
        auto v = base;
        for (auto& m : v) m *= n;
        vRet.push_back(v);
    }
    return vRet;
}

template<typename T>
struct sumGreaterThan
{
    T _limit{ 0 };

    auto operator()(const std::vector<T>& v) {return std::accumulate(v.cbegin(), v.cend(), 0) > _limit;}
};
   
int main()
{
    using namespace std;
    vector<int> nums{1,2,3,4,5,6,7,8,9};

    auto mults = buildMultiples(nums);

    for (auto& m : buildMultiples(nums)) {} //1. Compiles

    sumGreaterThan sumFilter{ 10 };
    
    auto vecs = buildMultiples(nums);
    for (auto& m : vecs | views::filter(sumFilter)) {} //2. Compiles

    for (auto& m : buildMultiples(nums) | views::filter(sumFilter)) {} //3. Compilation Error!!

    for (auto vecs = buildMultiples(nums); auto & m : vecs | views::filter(sumFilter)) {} // 4. Compiles. Thanks @Aryter
}

【问题讨论】:

  • 如果你使用const auto&amp;会怎样?
  • 如果你愿意也可以for (auto vecs = buildMultiples(nums); auto&amp; m : vecs | views::filter(sumFilter))
  • @Artyer 这种语法对我来说看起来很奇怪(我想知道您是否将; 输入错误,),直到进一步的研究表明它仅在C++20 中才有效。感谢您的建议!

标签: c++ std c++20


【解决方案1】:

这是将左值 vector 传递给 filter

vecs | views::filter(sumFilter)

而这是将右值vector 传递给filter

buildMultiples(nums) | views::filter(sumFilter)

编译器实现的当前规则是范围适配器管道不能采用右值非视图范围(如vectorstring 等)。这是因为管道本身是非拥有的(视图是非拥有的),并且作为防止悬空的安全机制而存在。

最近作为缺陷采用的新规则将允许这可能并且会导致filter拥有buildMultiples(这是P2415)的结果,但编译器不会'还没有实现它。通过此更改,您的其他版本也将被编译。

所以现在,你必须继续这样写(就像你已经在做的那样):

auto vecs = buildMultiples(nums);
for (auto& m : vecs | views::filter(sumFilter)) { ... }

【讨论】:

  • 我怀疑这是一个 R 值、L 值的东西,但我认为 buildMultiples() 的结果在循环期间是有效的,就像在版本 (1) 中一样。错误描述的措辞也有点混乱,因为版本(2)似乎提供了相同的 lhs 参数类型)。感谢您的清晰解释,以及语言演变的更新。
  • @DS_London 如果它只是通过引用捕获就不会是这样——并非所有基于范围的初始化器表达式都延长了它们的生命周期。如果视图实际上拥有buildMultiples 的结果,您的选择是(a) 不编译或(b) 有一个悬空引用。鉴于这种选择,我更喜欢(a)。虽然我更喜欢选项(c):让它做正确的事。这就是我们所做的:-)
  • @DS_London 让它更加混乱。 ranges::for_each(buildMultiplies(~) | views::filter(~), [&amp;](auto&amp; m){ ... }) 实际上会很好 - 它特别是基于范围的,因为它会无条件地悬空:-(
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-21
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多