C++ 将不可复制的闭包对象传递给std::function参数

C++ 将不可复制的闭包对象传递给std::function参数,c++,lambda,move-semantics,c++14,C++,Lambda,Move Semantics,C++14,在C++14中,lambda表达式可以通过使用捕获初始值设定项从变量中移动来捕获变量。但是,这使得生成的闭包对象不可复制。如果我有一个接受std::function参数(我不能更改)的现有函数,我就不能传递闭包对象,因为std::function的构造函数要求给定的functor是CopyConstructible #include <iostream> #include <memory> void doit(std::function<void()> f)

在C++14中,lambda表达式可以通过使用捕获初始值设定项从变量中移动来捕获变量。但是,这使得生成的闭包对象不可复制。如果我有一个接受
std::function
参数(我不能更改)的现有函数,我就不能传递闭包对象,因为
std::function
的构造函数要求给定的functor是
CopyConstructible

#include <iostream>
#include <memory>

void doit(std::function<void()> f) {
    f();
}

int main()
{
    std::unique_ptr<int> p(new int(5));
    doit([p = std::move(p)] () { std::cout << *p << std::endl; });
}
#包括
#包括
void doit(std::函数f){
f();
}
int main()
{
std::unique_ptr p(新int(5));

doit([p=std::move(p)](){std::cout如果闭包对象的生存期不是问题,则可以在引用包装器中传递它:

int main()
{
    std::unique_ptr<int> p(new int(5));
    auto f = [p = std::move(p)]{
        std::cout << *p << std::endl;
    };
    doit(std::cref(f));
}
intmain()
{
std::unique_ptr p(新int(5));
自动f=[p=std::move(p)]{

std::cout如果闭包对象的生存期不是问题,则可以在引用包装器中传递它:

int main()
{
    std::unique_ptr<int> p(new int(5));
    auto f = [p = std::move(p)]{
        std::cout << *p << std::endl;
    };
    doit(std::cref(f));
}
intmain()
{
std::unique_ptr p(新int(5));
自动f=[p=std::move(p)]{

std::cout有这种方法:

template< typename signature >
struct make_copyable_function_helper;
template< typename R, typename... Args >
struct make_copyable_function_helper<R(Args...)> {
  template<typename input>
  std::function<R(Args...)> operator()( input&& i ) const {
    auto ptr = std::make_shared< typename std::decay<input>::type >( std::forward<input>(i) );
    return [ptr]( Args... args )->R {
      return (*ptr)(std::forward<Args>(args)...);
    };
  }
};

template< typename signature, typename input >
std::function<signature> make_copyable_function( input && i ) {
  return make_copyable_function_helper<signature>()( std::forward<input>(i) );
}
更高级的版本推迟了类型擦除,并添加了一层完美的转发以减少开销:

template<typename input>
struct copyable_function {
  typedef typename std::decay<input>::type stored_input;
  template<typename... Args>
  auto operator()( Args&&... args )->
    decltype( std::declval<input&>()(std::forward<Args>(args)...) )
  {
    return (*ptr)(std::forward<Args>(args));
  }
  copyable_function( input&& i ):ptr( std::make_shared<stored_input>( std::forward<input>(i) ) ) {}
  copyable_function( copyable_function const& ) = default;
private:
  std::shared_ptr<stored_input> ptr;
};
template<typename input>
copyable_function<input> make_copyable_function( input&& i ) {
  return {std::forward<input>(i)}; 
}
模板
结构可复制函数{
typedef typename std::decage::type存储的\u输入;
模板
自动运算符()(Args&&…Args)->
decltype(std::declval()(std::forward(args)…)
{
返回(*ptr)(标准::转发(args));
}
可复制_函数(输入和&i):ptr(std::make_共享(std::forward(i)){
可复制函数(可复制函数常量&)=默认值;
私人:
std::共享的ptr;
};
模板
可复制功能使可复制功能(输入和输入){
返回{std::forward(i)};
}
它不需要您传入签名,在某些情况下可能会稍微更有效,但使用了更模糊的技术

在C++14中,这可以变得更加简短:

template< class F >
auto make_copyable_function( F&& f ) {
  using dF=std::decay_t<F>;
  auto spf = std::make_shared<dF>( std::forward<F>(f) );
  return [spf](auto&&... args)->decltype(auto) {
    return (*spf)( decltype(args)(args)... );
  };
}
模板
自动生成可复制功能(F&&F){
使用dF=std::decation\u t;
自动spf=std::使_共享(std::转发(f));
返回[spf](自动&&…参数)->decltype(自动){
返回(*spf)(decltype(args)(args)…);
};
}

完全不需要助手类型。

有一种方法:

template< typename signature >
struct make_copyable_function_helper;
template< typename R, typename... Args >
struct make_copyable_function_helper<R(Args...)> {
  template<typename input>
  std::function<R(Args...)> operator()( input&& i ) const {
    auto ptr = std::make_shared< typename std::decay<input>::type >( std::forward<input>(i) );
    return [ptr]( Args... args )->R {
      return (*ptr)(std::forward<Args>(args)...);
    };
  }
};

template< typename signature, typename input >
std::function<signature> make_copyable_function( input && i ) {
  return make_copyable_function_helper<signature>()( std::forward<input>(i) );
}
更高级的版本推迟了类型擦除,并添加了一层完美的转发以减少开销:

template<typename input>
struct copyable_function {
  typedef typename std::decay<input>::type stored_input;
  template<typename... Args>
  auto operator()( Args&&... args )->
    decltype( std::declval<input&>()(std::forward<Args>(args)...) )
  {
    return (*ptr)(std::forward<Args>(args));
  }
  copyable_function( input&& i ):ptr( std::make_shared<stored_input>( std::forward<input>(i) ) ) {}
  copyable_function( copyable_function const& ) = default;
private:
  std::shared_ptr<stored_input> ptr;
};
template<typename input>
copyable_function<input> make_copyable_function( input&& i ) {
  return {std::forward<input>(i)}; 
}
模板
结构可复制函数{
typedef typename std::decage::type存储的\u输入;
模板
自动运算符()(Args&&…Args)->
decltype(std::declval()(std::forward(args)…)
{
返回(*ptr)(标准::转发(args));
}
可复制_函数(输入和&i):ptr(std::make_共享(std::forward(i)){
可复制函数(可复制函数常量&)=默认值;
私人:
std::共享的ptr;
};
模板
可复制功能使可复制功能(输入和输入){
返回{std::forward(i)};
}
它不需要您传入签名,在某些情况下可能会稍微更有效,但使用了更模糊的技术

在C++14中,这可以变得更加简短:

template< class F >
auto make_copyable_function( F&& f ) {
  using dF=std::decay_t<F>;
  auto spf = std::make_shared<dF>( std::forward<F>(f) );
  return [spf](auto&&... args)->decltype(auto) {
    return (*spf)( decltype(args)(args)... );
  };
}
模板
自动生成可复制功能(F&&F){
使用dF=std::decation\u t;
自动spf=std::使_共享(std::转发(f));
返回[spf](自动&&…参数)->decltype(自动){
返回(*spf)(decltype(args)(args)…);
};
}

完全不需要helper类型。

如果你知道你实际上不打算复制你的函数对象,那么你可以把它包装成一个类型,让编译器认为它是可复制的:

struct ThrowOnCopy {
  ThrowOnCopy() = default;
  ThrowOnCopy(const ThrowOnCopy&) { throw std::logic_error("Oops!"); }
  ThrowOnCopy(ThrowOnCopy&&) = default;
  ThrowOnCopy& operator=(ThrowOnCopy&&) = default;
};

template<typename T>
  struct FakeCopyable : ThrowOnCopy
  {
    FakeCopyable(T&& t) : target(std::forward<T>(t)) { }

    FakeCopyable(FakeCopyable&&) = default;

    FakeCopyable(const FakeCopyable& other)
    : ThrowOnCopy(other),                             // this will throw
      target(std::move(const_cast<T&>(other.target))) // never reached
    { }

    template<typename... Args>
      auto operator()(Args&&... a)
      { return target(std::forward<Args>(a)...); }

    T target;
  };


template<typename T>
  FakeCopyable<T>
  fake_copyable(T&& t)
  { return { std::forward<T>(t) }; }


// ...

doit( fake_copyable([p = std::move(p)] () { std::cout << *p << std::endl; }) );
struct ThrowOnCopy{
ThrowOnCopy()=默认值;
ThrowOnCopy(const ThrowOnCopy&){throw std::logic_error(“Oops!”);}
ThrowOnCopy(ThrowOnCopy&&)=默认值;
ThrowOnCopy&运算符=(ThrowOnCopy&&)=默认值;
};
模板
结构伪造可复制:ThrowOnCopy
{
FakeCopyable(T&&T):目标(std::forward(T)){
FakeCopyable(FakeCopyable&&)=默认值;
伪造复制品(常量伪造复制品和其他)
:ThrowOnCopy(其他),//这将抛出
目标(std::move(const_cast(other.target))//从未到达
{ }
模板
自动运算符()(Args&&…a)
{返回目标(std::forward(a)…);}
T靶;
};
模板
伪造的
假冒可复制(T&T)
{return{std::forward(t)};}
// ...

doit(fake_copyable([p=std::move(p)](){std::cout如果你知道你实际上不打算复制你的函数对象,那么你可以把它包装成一个让编译器认为它是可复制的类型:

struct ThrowOnCopy {
  ThrowOnCopy() = default;
  ThrowOnCopy(const ThrowOnCopy&) { throw std::logic_error("Oops!"); }
  ThrowOnCopy(ThrowOnCopy&&) = default;
  ThrowOnCopy& operator=(ThrowOnCopy&&) = default;
};

template<typename T>
  struct FakeCopyable : ThrowOnCopy
  {
    FakeCopyable(T&& t) : target(std::forward<T>(t)) { }

    FakeCopyable(FakeCopyable&&) = default;

    FakeCopyable(const FakeCopyable& other)
    : ThrowOnCopy(other),                             // this will throw
      target(std::move(const_cast<T&>(other.target))) // never reached
    { }

    template<typename... Args>
      auto operator()(Args&&... a)
      { return target(std::forward<Args>(a)...); }

    T target;
  };


template<typename T>
  FakeCopyable<T>
  fake_copyable(T&& t)
  { return { std::forward<T>(t) }; }


// ...

doit( fake_copyable([p = std::move(p)] () { std::cout << *p << std::endl; }) );
struct ThrowOnCopy{
ThrowOnCopy()=默认值;
ThrowOnCopy(const ThrowOnCopy&){throw std::logic_error(“Oops!”);}
ThrowOnCopy(ThrowOnCopy&&)=默认值;
ThrowOnCopy&运算符=(ThrowOnCopy&&)=默认值;
};
模板
结构伪造可复制:ThrowOnCopy
{
FakeCopyable(T&&T):目标(std::forward(T)){
FakeCopyable(FakeCopyable&&)=默认值;
伪造复制品(常量伪造复制品和其他)
:ThrowOnCopy(其他),//这将抛出
目标(std::move(const_cast(other.target))//从未到达
{ }
模板
自动运算符()(Args&&…a)
{返回目标(std::forward(a)…);}
T靶;
};
模板
伪造的
假冒可复制(T&T)
{return{std::forward(t)};}
// ...

doit(fake_copyable([p=std::move(p)]({std::cout)如果对象的闭包不是问题,您能不能让doit接受一个const函数&?@MadScienceDreams它可以与一个特定的实现一起工作,将函数对象构造为
std::function bar{std::move(f)};
如果该实现避免实例化
std::function
的复制构造函数,那么它将不是可移植的行为,因为
std::function
明确要求functor在[func.wrap.func.con]中可复制构造/7.事实上,.@MadScienceDreams我假设
std::function
的实现通过用