在此示例中,(Immediately Invoked Function Expression)
实例不执行任何操作,仅打印其是复制构造还是移动构造。
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; });
}
问题:
move constructed
copy constructed
move constructed
move constructed
copy constructed
copy constructed
创建这么多的闭包?编译器为gcc 8.1.1
答案 0 :(得分:7)
如果我们看一下the implementation of std::remove_if
in gcc's libstdc++-v3,则会注意到谓词在到达最低的__find_if
函数(由{{ 1}})。
让我们计算一下移动和复制:
remove_if
,当谓词(包括捕获的move constructed
)通过值(但作为非左值)发送到std::remove_if
entry point
x
传递给__gnu_cxx::__ops::__pred_iter(...)
函数时,该函数依次为:
调用_GLIBCXX_MOVE
macro,因此调用copy constructed
,
将该谓词移至_Iter_pred
ctor,该谓词(move constructed
)移入move constructed
成员。
从_M_pred
到std::__remove_if
的调用似乎已经过优化,因为std::remove_if
不是左值,但是_Iter_pred
依次通过将包装的谓词按值划分为std::__find_if
,以进行另一个__remove_if
调用。
copy constructed
依次按值将包装的谓词转发到another __find_if
overload,该谓词最终是该调用链的接收器,也是最终的std::__find_if
。 / p>
与例如clang的实现为copy constructed
,因为clang(6.0.1)不会为OP的std::remove_if
示例生成此移动副本链。快速浏览一下,似乎似乎clang use traits on the predicates type确保通过谓词as an lvalue reference。
clang和gcc都为下面的人为示例生成相同的std::remove_if
/ move
链,该链显示了与gcc的实现类似的链:
copy
在gcc(8.2.0)和clang(6.0.1)都产生以下链的情况下:
#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; });
}