I am trying to move a functor into a lambda inside an object, like this:
#include <functional>
#include <iostream>
#include "boost/stacktrace.hpp"
#define fwd(o) std::forward<decltype(o)>(o)
struct CopyCounter {
CopyCounter() noexcept = default;
CopyCounter(const CopyCounter &) noexcept {
std::cout << "Copied at " << boost::stacktrace::stacktrace() << std::endl;
counter++;
}
CopyCounter(CopyCounter &&) noexcept = default;
CopyCounter &operator=(CopyCounter &&) noexcept = default;
CopyCounter &operator=(const CopyCounter &) noexcept {
std::cout << "Copied at " << boost::stacktrace::stacktrace() << std::endl;
counter++;
return *this;
}
inline static size_t counter = 0;
};
struct Argument : CopyCounter {};
struct Functor : CopyCounter {
int operator()(Argument) { return 42; }
};
class Invoker {
std::function<void()> invoke_;
int result_;
public:
template <class Functor, class... Args>
Invoker(Functor&& f, Args&&... args) {
invoke_ = [this, f = fwd(f), ...args = fwd(args)]() mutable {
result_ = f(fwd(args)...);
};
}
};
int main() {
Functor f;
Argument a;
auto i = Invoker(std::move(f), std::move(a));
assert(CopyCounter::counter == 0);
return 0;
}
Somewhat surprisingly, the last assert fails on libc++, but not libstdc++. The stacktrace hints at the two copies that are performed:
Copied at 0# CopyCounter at /usr/include/boost/stacktrace/stacktrace.hpp:?
1# 0x00000000004C812E at ./src/csc_cpp/move_functors.cpp:38
2# std::__1::__function::__value_func<void ()>::swap(std::__1::__function::__value_func<void ()>&) at /usr/lib/llvm-10/bin/../include/c++/v1/functional:?
3# ~__value_func at /usr/lib/llvm-10/bin/../include/c++/v1/functional:1825
4# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
5# _start in ./bin/./src/csc_cpp/move_functors
Copied at 0# CopyCounter at /usr/include/boost/stacktrace/stacktrace.hpp:?
1# std::__1::__function::__value_func<void ()>::swap(std::__1::__function::__value_func<void ()>&) at /usr/lib/llvm-10/bin/../include/c++/v1/functional:?
2# ~__value_func at /usr/lib/llvm-10/bin/../include/c++/v1/functional:1825
3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
4# _start in ./bin/./src/csc_cpp/move_functors
It seems like inside the library the functor and the argument get copied in swap
, during the move-assignment of invoke_
. There are two questions:
- Why is this the desired behaviour and what can be the motivation behind this design solution?
- What is a good way to update the code to reach the same semantics as in libstdc++?
Aucun commentaire:
Enregistrer un commentaire