C++ 如何测试琐碎的复制可分配lambda

C++ 如何测试琐碎的复制可分配lambda,c++,c++11,lambda,C++,C++11,Lambda,如果有人能给我一个提示,告诉我如何测试函子的简单复制能力,我将不胜感激。正如在中所解释的,实现定义了lambda是否是可复制的。例如,对于本问题末尾所示的代码,gcc(5.4)和msvc(2015)都提出了这样的断言,即它们不是简单的复制可分配的 我希望这些类型的lambda由struct表示,保留this指针,并拥有每个捕获值(如果有)的副本。所以它们看起来都是可复制的——至少在捕获的值可复制的情况下是如此 我提出这个问题的真正用例是,我正在使用一个回调(不分配的std::function的最

如果有人能给我一个提示,告诉我如何测试函子的简单复制能力,我将不胜感激。正如在中所解释的,实现定义了lambda是否是可复制的。例如,对于本问题末尾所示的代码,gcc(5.4)和msvc(2015)都提出了这样的断言,即它们不是简单的复制可分配的

我希望这些类型的lambda由
struct
表示,保留
this
指针,并拥有每个捕获值(如果有)的副本。所以它们看起来都是可复制的——至少在捕获的值可复制的情况下是如此

我提出这个问题的真正用例是,我正在使用一个回调(不分配的
std::function
的最低版本,用于非常简单的函子),它保留一个固定的缓冲区,在其中复制(就地)传递的函子的结构。然后,我希望能够复制/分配这些回调,但为了实现这一点(开箱即用),这些固定缓冲区的简单mem复制应该相当于保留在其中的函子的处理/分配

所以我有两个问题:

  • 想象一下,在下面的
    test\u functor()
    中,我做了
    new
    放置,例如

    new(&buffer)F(functor)

    对于下面所示的lambda类型,只使用
    memcopy
    这个缓冲区安全吗?我认为应该是这样的,因为在所有情况下,只捕获了
    这个
    指针,或者捕获的值都是可复制的,但是如果有人能够确认这一点,那就太好了

  • 如何测试保存函子的内存的简单复制是否等同于函子的复制?如果第一个问题的答案是肯定的,那么
    std::就不是正确的答案

  • #包括
    模板
    无效测试函数(常数F&函数)
    {
    静态断言(std::是可破坏的::值,
    “不可平凡破坏的函子”);
    静态断言(std::是可构造的::值,
    “函子不容易复制可构造”);
    静态断言(std::is_琐碎地\u复制\u可赋值::值,
    “函子不平凡地复制可赋值”);
    }
    结构A
    {
    void test(){test_函子([this](){});}
    };
    结构B
    {
    void test(){test_函子([this](int v){value=v;});}
    int值;
    };
    结构C
    {
    无效测试(intv){test_函子([=](){value=v;});}
    int值;
    };
    int main()
    {
    A A;
    B B;
    C C;
    a、 test();
    b、 test();
    c、 试验(1);
    返回0;
    }
    
    不,这不安全。如果编译器说某些东西不能被简单地复制,那么它就不能被复制

    它可能会起作用。但这并不意味着它是安全的

    即使它今天可以工作,但明天在编译器更新一段时间后就停止工作了

    解决方法非常简单。编写一个SBO类型(小缓冲区优化),它不需要简单的可复制性

    template<std::size_t S, std::size_t A>
    struct SBO {
      void(*destroy)(SBO*) = nullptr;
    //  void(*copy_ctor)(SBO const* src, SBO* dest) = nullptr;
      void(*move_ctor)(SBO* src, SBO* dest) = nullptr;
      std::aligned_storage_t< S, A > buffer;
    
      void clear() {
        auto d = destroy;
        destroy = nullptr;
      //  copy_ctor = nullptr;
        move_ctor = nullptr;
        if (d) d(this);
      }
    
      template<class T, class...Args>
      T* emplace( Args&&... args ) {
        static_assert( sizeof(T) <= S && alignof(T) <= A, "not enough space or alignment" );
        T* r = new( (void*)&buffer ) T(std::forward<Args>(args)...);
        destroy = [](SBO* buffer) {
          ((T*)&buffer->buffer)->~T();
        };
        // do you need a copy ctor?  If not, don't include this:
        //copy_ctor = [](SBO const* src, SBO* dest) {
        //  auto s = (T const*)&src.buffer;
        //  dest->clear();
        //  dest->emplace<T>( *s );
        //};
        move_ctor = [](SBO* src, SBO* dest) {
          auto* s = (T*)&src->buffer;
          dest->clear();
          dest->emplace<T>( std::move(*s) );
          src->clear();
        };
        return r;
      }
      SBO() = default;
      SBO(SBO&& o) {
        if (o.move_ctor) {
          o.move_ctor(&o, this);
        }
      }
      SBO& operator=(SBO&& o) {
        if (this == &o) return *this; // self assign clear, which seems surprising
        if (o.move_ctor) {
          o.move_ctor(&o, this);
        }
        return *this;
      }
      // do you need a copy ctor?  If so, implement `SBO const&` ctor/assign
    };
    
    模板
    结构SBO{
    无效(*销毁)(SBO*)=无效PTR;
    //无效(*副本)(SBO const*src,SBO*dest)=nullptr;
    无效(*移动系数)(SBO*src,SBO*dest)=空PTR;
    std::对齐存储\u t缓冲区;
    无效清除(){
    自动d=销毁;
    销毁=空PTR;
    //复制系数=nullptr;
    move_ctor=nullptr;
    若(d)d(本);
    }
    模板
    T*定位(Args&&…Args){
    
    您应该使用的静态断言(sizeof(T)是可复制的,而不是可构造的或可分配的。lambda从来都不是可复制的(或移动的)因为他们的复制赋值操作符被删除了,所以是可赋值的;但这并不一定会阻止他们的可复制性。@t.C.-你能在“答案”部分发表你的评论吗?我想把它标记为答案,因为你一针见血。我不知道lambdas的复制赋值被删除了。事实上,
    是不可复制的pyable
    正在通过。谢谢你。谢谢你的例子。我会看一下,但同时我会保留它们(我在测试
    std::function
    ——我想是用GCC4.9——它是为我正在使用的lambdas分配的)。请看T.C的评论——这与你的第一句话非常吻合:)-我应该通过
    std::is\u littlely\u copyable
    测试琐碎的可复制性regards@AndrzejO笑!我错过了。我觉得编译器有点傻。不过,如果它是个傻瓜,你不会说“我真的知道发生了什么”雅克,我决定把你的答案标记为一个解决方案,因为MSVC似乎仍然声称这样的lambda不是简单的可复制的。我打赌编译器会出错,但在这种情况下,我别无选择,只能自己复制。
    template<std::size_t S, std::size_t A>
    struct SBO {
      void(*destroy)(SBO*) = nullptr;
    //  void(*copy_ctor)(SBO const* src, SBO* dest) = nullptr;
      void(*move_ctor)(SBO* src, SBO* dest) = nullptr;
      std::aligned_storage_t< S, A > buffer;
    
      void clear() {
        auto d = destroy;
        destroy = nullptr;
      //  copy_ctor = nullptr;
        move_ctor = nullptr;
        if (d) d(this);
      }
    
      template<class T, class...Args>
      T* emplace( Args&&... args ) {
        static_assert( sizeof(T) <= S && alignof(T) <= A, "not enough space or alignment" );
        T* r = new( (void*)&buffer ) T(std::forward<Args>(args)...);
        destroy = [](SBO* buffer) {
          ((T*)&buffer->buffer)->~T();
        };
        // do you need a copy ctor?  If not, don't include this:
        //copy_ctor = [](SBO const* src, SBO* dest) {
        //  auto s = (T const*)&src.buffer;
        //  dest->clear();
        //  dest->emplace<T>( *s );
        //};
        move_ctor = [](SBO* src, SBO* dest) {
          auto* s = (T*)&src->buffer;
          dest->clear();
          dest->emplace<T>( std::move(*s) );
          src->clear();
        };
        return r;
      }
      SBO() = default;
      SBO(SBO&& o) {
        if (o.move_ctor) {
          o.move_ctor(&o, this);
        }
      }
      SBO& operator=(SBO&& o) {
        if (this == &o) return *this; // self assign clear, which seems surprising
        if (o.move_ctor) {
          o.move_ctor(&o, this);
        }
        return *this;
      }
      // do you need a copy ctor?  If so, implement `SBO const&` ctor/assign
    };