康桓瑋*_*康桓瑋 20 c++ language-design language-lawyer c++20 std-ranges
传统的std::for_each返回函数作为标准只需要根据[alg.foreach]Function满足Cpp17MoveConstructible:
\n\nRun Code Online (Sandbox Code Playgroud)\ntemplate<class InputIterator, class Function>\n constexpr Function for_each(InputIterator first, InputIterator last, Function f);\n前提条件:
\nFunction满足Cpp17MoveConstructible要求。[注意:\xe2\x80\x82
\nFunction不需要满足Cpp17CopyConstructible的要求。尾注]
这是合理的,因为用户可能希望在调用后重用该函数。
\n的并行版本for_each没有返回:
\n\nRun Code Online (Sandbox Code Playgroud)\ntemplate<class ExecutionPolicy, class ForwardIterator, class Function>\n void for_each(ExecutionPolicy&& exec,\n ForwardIterator first, ForwardIterator last,\n Function f);\n前提条件:
\nFunction满足Cpp17CopyConstructible要求。
这是因为标准要求Function满足Cpp17CopyConstructible,因此不需要返回该函数,因为用户可以根据需要在调用方自由创建副本。
我注意到ranges::for_each它还返回该函数:
\n\nRun Code Online (Sandbox Code Playgroud)\ntemplate<input_iterator I, sentinel_for<I> S, class Proj = identity,\n indirectly_unary_invocable<projected<I, Proj>> Fun>\n constexpr ranges::for_each_result<I, Fun>\n ranges::for_each(I first, S last, Fun f, Proj proj = {});\n
然而,函数签名已经要求Fun满足indirectly_unary_invocable,这已经保证了它是可复制构造的。
问题是,为什么ranges::for_each仍然返回该函数?这样做有什么意义?
Hol*_*Cat 25
它返回函子,因为这允许在当时使用有状态函子进行一些巧妙的技巧(我认为是在 C++98 中)。现在您不会经常看到这些,因为 lambda 通常更简单。
这是一个例子:
#include <algorithm>
#include <iostream>
struct EvenCounter
{
int count;
EvenCounter() : count(0) {}
void operator()(int x)
{
if (x % 2 == 0)
count++;
}
};
int main()
{
int array[] = {1,2,3,4,5};
int num_even = std::for_each(array, array+5, EvenCounter()).count;
std::cout << num_even << '\n';
}
Run Code Online (Sandbox Code Playgroud)
这是合理的,因为用户可能希望在调用后重用该函数。
我认为这里的逻辑是倒退的。该函数不需要可复制,只是因为没有理由for_each复制它。
如果您有一个不可复制(甚至不可移动)的函数,您可以通过引用传递它std::ref以避免复制/移动,因此您不会从将函数返回给您的算法中获得任何好处。
C++98 中没有std::ref,但也没有移动语义,因此for_each一开始就无法使用不可复制的函子。
Bar*_*rry 12
\n\n问题是,为什么
\nranges::for_each仍然返回该函数?这样做有什么意义?
这里的答案很简单:所有算法要么(a)返回与(例如,,等)std::ranges::meow相同的东西,要么(b)另外返回新的高级输入迭代器(例如,如果返回有用的话)。从技术上讲,一些返回值的函数(例如,)也可能从返回结束迭代器中受益,但这对这些函数来说是一个巨大的人体工程学打击,所以这种情况没有发生。std::meowcountfindcopycount
值得注意的是,它们要么 (a) 保留,要么 (b) 添加到现有的返回类型。
\n没有人愿意仔细检查并实际更改算法的返回类型,并在 Ranges 已经完成的大量工作之上做出额外的设计决策。正如我们最喜欢的猫已经指出的那样,std::for_each在 C++11 中返回函数对象本身已经是不必要的,因为std::ref如果您确实想要的话,您可以通过简单地使用来完成有状态的事情 - 但似乎不值得弃用这一点更改std::for_each为 return ,然后加倍地认为,成为一个在行为上与其对应算法实际上不同的\xe2\x80\xa0算法void似乎并不值得。std::ranges::for_eachstd::
\xe2\x80\xa0好吧,从技术上讲并不是唯一的。std::ranges::copy指定发生了多少次迭代器增量而std::copy没有发生。我在今年的 CppNow 演讲中碰巧谈到了这一点:take(5).
| 归档时间: |
|
| 查看次数: |
1769 次 |
| 最近记录: |