在这个例子中,一个foo实例除了打印它是复制还是移动构造之外什么都不做.
#include <iostream>
#include <algorithm>
#include <vector>
struct foo {
foo()=default;
foo(foo &&) { std::cout << "move constructed\n"; }
foo(const foo &) { std::cout << "copy constructed\n"; }
};
int main()
{
foo x;
std::vector<int> v; // empty
std::remove_if(v.begin(), v.end(),
[x=std::move(x)](int i){ return false; });
}
Run Code Online (Sandbox Code Playgroud)
这会产生以下输出:
move constructed
copy constructed
move constructed
move constructed
copy constructed
copy constructed
Run Code Online (Sandbox Code Playgroud)
问题:
std::remove_if创建这么多的闭包?编译器是gcc 8.1.1
如果我们看一下gcc的libstdc ++ - v3 的实现,std::remove_if我们注意到谓词在到达最下面的__find_if函数(由...使用remove_if)之前向下传递调用链(通过值,有时)几步.
让我们算一下动作和副本:
move constructed当谓词(包括捕获的x)通过值发送,但作为非左值发送到std::remove_if入口点时
copy constructed当传递给__gnu_cxx::__ops::__pred_iter(...)函数时,反过来:
调用_GLIBCXX_MOVE宏,因此move constructed,
它将谓词移动到_Iter_predctor,后者将it(move constructed)移动到_M_pred成员中.
从电话std::remove_if到std::__remove_if似乎被优化,因为_Iter_pred不是左值,我猜,但__remove_if反过来通过对包装的谓词,通过价值,std::__find_if为另一个copy constructed调用.
std::__find_if反过来,将包装的谓词按值转发到另一个__find_if重载,最后是该调用链的接收器和最终的重载copy constructed.
它可以是有趣的与如铛的比较实现的std::remove_if,如铛(6.0.1),不产生对OP的这一举动复制链std::remove_if的例子.快速浏览显示,似乎clang在谓词类型上使用traits以确保将谓词作为左值引用传递.
clang和gcc都为后面的设计示例生成相同的move/ copy链,这显示了与gcc实现类似的链:
#include <iostream>
#include <utility>
struct foo {
foo() = default;
foo(foo &&) { std::cout << "move constructed\n"; }
foo(const foo &) { std::cout << "copy constructed\n"; }
};
template <typename Pred>
struct IterPred {
Pred m_pred;
explicit IterPred(Pred pred) : m_pred(std::move(pred)) {}
};
template <typename T>
inline IterPred<T> wrap_in_iterpred (T l) {
return IterPred<T>(std::move(l));
}
template <typename T>
void find_if_overload(T l) {
(void)l;
}
template <typename T>
void find_if_entrypoint(T l) {
find_if_overload(l);
}
template <typename T>
void remove_if_entrypoint(T l) {
find_if_entrypoint(
wrap_in_iterpred(l));
}
int main()
{
foo x;
remove_if_entrypoint([x=std::move(x)](int){ return false; });
}
Run Code Online (Sandbox Code Playgroud)
gcc(8.2.0)和clang(6.0.1)都产生以下链:
move constructed
copy constructed
move constructed
move constructed
copy constructed
Run Code Online (Sandbox Code Playgroud)