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; }
};
template <class Result>
class Invoker {
  std::function<void()> invoke_;
  Result* result_ = nullptr;
  template <class Functor, class... Args>
  Invoker(Functor&& f, Args&&... args) {
    if constexpr (std::is_same_v<Result, void>) {
      invoke_ = [this, f = fwd(f), ... args = fwd(args)]() mutable {
        f(fwd(args)...);
      };
    } else {
      invoke_ = [this, f = fwd(f), ...args = fwd(args)]() mutable { 
        result_ = new Result(f(fwd(args)...));
      };
    }
  }
  template <class Functor, class... Args>
  friend auto make_invoker(Functor&& f, Args&&... args);
public:
  ~Invoker() {
    if (result_) delete result_;
  }
};
template <class Functor, class... Args>
auto make_invoker(Functor&& f, Args&&... args) {
  return Invoker<decltype(f(args...))>(fwd(f), fwd(args)...);
}
int main() {
  Functor f;
  Argument a;
  auto i = make_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++?