无法使用 std::ranges::copy_if(R&& r, O result, Pred pred, Proj proj = {}) 编译我的示例
Can't make my example using std::ranges::copy_if(R&& r, O result, Pred pred, Proj proj = {}) compile
我已经将无法编译的代码减少为以下代码:
#include <algorithm> // copy_if
#include <iostream> // cout
#include <iterator> // back_inserter
#include <ranges>
#include <string>
#include <vector>
struct A
{
std::string p{};
std::string d{};
};
int main()
{
std::vector<A> v{{"/usr/bin/cat", "blah"}, {"/usr/lib", "foo"}};
std::vector<std::string> o{};
std::ranges::copy_if(
v,
std::back_inserter(o),
[](std::string& p){ return (p.size() > 10); },
&A::p);
}
Visual Studio 的错误输出很短:error C7602: 'std::ranges::_Copy_if_fn::operator ()': the associated constraints are not satisfied.
它基本上指示您在 algorithm
header 处检查 copy_if
的约束。
gcc和clang更详细。看一下两个输出:
- 首先,考虑带有范围、输出迭代器、谓词和投影的
copy_if
重载。
- 但是,表达式
*__o = (std::forward<_Tp>(__t)
被评估为无效;其中:'_Out&& __o', '_Tp&& __t' [with _Tp = A&; _Out = std::back_insert_iterator<std::vector<std::string>>]
既然我们使用的是投影(从 A&
到 std::string&
),为什么它试图复制一个 A&
而不是 std::string&
到 o
?
完整的错误输出可通过上面的演示 link 访问。
投影仅适用于谓词,不适用于结果。已经有一种方法可以完全转换数据—std::views::transform
:
std::ranges::copy_if(
v | std::views::transform(&A::p),
std::back_inserter(o),
[](const std::string& p){ return (p.size() > 10); });
(这可以在 GCC 和 MSVC 中编译,但不能在 Clang 中编译。我不是 100% 有信心说它是正确的,但如果不出意外的话应该很接近,它说明了这一点。)
投影适用于您想要测试数据的转换(投影),同时携带原件。在此示例中,这意味着您将输出到 std::vector<A>
,但在谓词中测试 std::string
。在这里不是很有用,因为你可以 return (a.p.size() > 10);
,但如果传递 pre-written 函数而不是使用 lambda,则很有用。对于其他一些情况也很有用,例如按成员轻松排序——只需使用默认比较器并传递 &A::p
作为投影。
Since we are using a projection (from A& to std::string&), why is it trying to copy an A& instead of a std::string& to o?
因为算法就是这么做的。 copy_if
对于每个满足谓词的元素,总是将源范围复制到提供的迭代器中。
投影只影响谓词 - 它基本上只是在这里进行函数组合 - 但它不会影响整个算法的作用。
所有投影都是如此。
如果您想要复制的只是 p
个子对象,您想要做的是
ranges::copy_if(v | views::transform(&A::p), /* ... */);
现在更改了您要从中复制的源范围。
有关此主题的更多信息,我写了一个 whole post 关于它。
我已经将无法编译的代码减少为以下代码:
#include <algorithm> // copy_if
#include <iostream> // cout
#include <iterator> // back_inserter
#include <ranges>
#include <string>
#include <vector>
struct A
{
std::string p{};
std::string d{};
};
int main()
{
std::vector<A> v{{"/usr/bin/cat", "blah"}, {"/usr/lib", "foo"}};
std::vector<std::string> o{};
std::ranges::copy_if(
v,
std::back_inserter(o),
[](std::string& p){ return (p.size() > 10); },
&A::p);
}
Visual Studio 的错误输出很短:error C7602: 'std::ranges::_Copy_if_fn::operator ()': the associated constraints are not satisfied.
它基本上指示您在 algorithm
header 处检查 copy_if
的约束。
gcc和clang更详细。看一下两个输出:
- 首先,考虑带有范围、输出迭代器、谓词和投影的
copy_if
重载。 - 但是,表达式
*__o = (std::forward<_Tp>(__t)
被评估为无效;其中:'_Out&& __o', '_Tp&& __t' [with _Tp = A&; _Out = std::back_insert_iterator<std::vector<std::string>>]
既然我们使用的是投影(从 A&
到 std::string&
),为什么它试图复制一个 A&
而不是 std::string&
到 o
?
完整的错误输出可通过上面的演示 link 访问。
投影仅适用于谓词,不适用于结果。已经有一种方法可以完全转换数据—std::views::transform
:
std::ranges::copy_if(
v | std::views::transform(&A::p),
std::back_inserter(o),
[](const std::string& p){ return (p.size() > 10); });
(这可以在 GCC 和 MSVC 中编译,但不能在 Clang 中编译。我不是 100% 有信心说它是正确的,但如果不出意外的话应该很接近,它说明了这一点。)
投影适用于您想要测试数据的转换(投影),同时携带原件。在此示例中,这意味着您将输出到 std::vector<A>
,但在谓词中测试 std::string
。在这里不是很有用,因为你可以 return (a.p.size() > 10);
,但如果传递 pre-written 函数而不是使用 lambda,则很有用。对于其他一些情况也很有用,例如按成员轻松排序——只需使用默认比较器并传递 &A::p
作为投影。
Since we are using a projection (from A& to std::string&), why is it trying to copy an A& instead of a std::string& to o?
因为算法就是这么做的。 copy_if
对于每个满足谓词的元素,总是将源范围复制到提供的迭代器中。
投影只影响谓词 - 它基本上只是在这里进行函数组合 - 但它不会影响整个算法的作用。
所有投影都是如此。
如果您想要复制的只是 p
个子对象,您想要做的是
ranges::copy_if(v | views::transform(&A::p), /* ... */);
现在更改了您要从中复制的源范围。
有关此主题的更多信息,我写了一个 whole post 关于它。