C++ 带有标准布局结构的RVO,无任何构造函数

C++ 带有标准布局结构的RVO,无任何构造函数,c++,c++11,rvo,C++,C++11,Rvo,我有一个表示二进制消息的结构。我想编写一个函数,从缓冲区中获取下一条这样的记录(无论是文件还是套接字,都无所谓): 缓冲区直接读入记录的内存中。这可能吗 no_init是类型为no_init\t的常量 如果您从一个no\u init\t构建一个pod,您将得到一个未初始化的pod,并且(假设省略)没有什么要做的 如果从no_init\t构造非pod,则必须重写构造函数,并使其不初始化数据。通常class_name(no_init_t):field1(no_init)、field2(no_init

我有一个表示二进制消息的结构。我想编写一个函数,从缓冲区中获取下一条这样的记录(无论是文件还是套接字,都无所谓):


缓冲区直接读入
记录的内存中。这可能吗

no_init
是类型为
no_init\t
的常量

如果您从一个
no\u init\t
构建一个pod,您将得到一个未初始化的pod,并且(假设省略)没有什么要做的

如果从
no_init\t
构造非pod,则必须重写构造函数,并使其不初始化数据。通常
class_name(no_init_t):field1(no_init)、field2(no_init){}
将执行此操作,有时
class_name(no_init_t){}
将执行此操作(假设所有内容都是pod)

然而,在每个成员上构建from
no_init
可以作为一个健全的检查来检查成员是否确实是pod。由
no_init
构造的非pod类将无法编译,直到您编写
no_init\t
构造函数

这(必须
no_init
每个成员构造函数)确实会产生一些恼人的干故障,但我们没有反射,所以您要重复自己并喜欢它

namespace {
  struct no_init_t {
    template<class T, class=std::enable_if_t<std::is_pod<T>{}>>
    operator T()const{
      T tmp;
      return tmp;
    }
    static no_init_t instance() { return {}; }
    no_init_t(no_init_t const&) = default;
  private:
    no_init_t() = default;
  };
  static const no_init = no_init_t::instance();
}


struct Foo {
  char buff[1000];
  size_t hash;
  Foo():Foo(""){}
  template<size_t N, class=std::enable_if_t< (N<=sizeof(buff)) >>
  Foo( char const(&in)[N] ) {
    // some "expensive" copy and hash
  }
  Foo(no_init_t) {} // no initialization!
};
struct Record {
  int x;
  Foo foo;
  Record()=default;
  Record(no_init_t):
    x(no_init), foo(no_init)
  {}
};
名称空间{
结构no\u init\t{
模板
运算符T()常量{
T-tmp;
返回tmp;
}
静态no_init_t instance(){return{};}
no_init_t(no_init_t const&)=默认值;
私人:
no_init_t()=默认值;
};
静态常量no_init=no_init_t::instance();
}
结构Foo{
字符增益[1000];
大小散列;
Foo():Foo(“”{}
模板
Foo(字符常量(&in)[N]){
//一些“昂贵的”拷贝和散列
}
Foo(no_init_t){}//没有初始化!
};
结构记录{
int x;
富富,;
记录()=默认值;
记录(无初始):
x(无初始),foo(无初始)
{}
};
现在我们可以用
no_init
构造
Record
,它不会被初始化

每个POD类都没有初始化。每个非POD类必须提供一个
no\u init\t
构造函数(并尽可能实现非初始化)

然后,您将
memcpy
放在它的正上方

这需要修改您的类型及其包含的类型,以支持非初始化。

类似的内容

编辑:

  • 解决对对齐的评论。现在使用匿名联合来确保正确对齐

  • TestRecord现在合并了另一种标准布局类型
    egg

  • 添加了证据,证明即使
    egg
    有一个默认构造函数,该类在被
    populateNextRecord()

  • 我想这是最快的了,不是吗

    #include <iostream>
    #include <array>
    #include <algorithm>
    
    struct egg {
        egg(int i) : _val(i) {}
        egg() {}
        int _val = 6;    
        friend std::ostream& operator<<(std::ostream& os, const egg& e) {
            return os << e._val; 
        }
    };
    
    struct TestRecord {
        egg x;
        double y;
    };
    
    void populateNext(uint8_t* first, size_t length)
    {
        // do work here
        TestRecord data_source { 10, 100.2 };
        auto source = reinterpret_cast<uint8_t*>(&data_source);
        std::copy(source, source + length, first);
    }
    
    template<class Record>
    struct RecordProxy
    {
        RecordProxy() {}
    
      uint8_t* data() {
          return _data;
      }
    
      static constexpr size_t size() {
          return sizeof(Record);
      }
    
      Record& as_record() {
          return _record;
      }
    
        union {
            Record _record;
            uint8_t _data[sizeof(Record)];
        };
    };
    
    
    template <typename Record>
    RecordProxy<Record> getNext() {
        RecordProxy<Record> r;
        populateNext(r.data(),  // maybe ::read()
                     r.size());                   // or equivalent
        return r;
    }
    
    using namespace std;
    int main()
    {
        RecordProxy<TestRecord> prove_not_initialised;
        auto& r1 = prove_not_initialised.as_record();
        cout << "x = " << r1.x << ", y = " << r1.y << endl;
    
        auto buffer = getNext<TestRecord>();
        auto& actual_record = buffer.as_record();
        cout << "x = " << actual_record.x << ", y = " << actual_record.y << endl;
       return 0;
    }
    
    #包括
    #包括
    #包括
    结构蛋{
    鸡蛋(国际一级):_val(i){}
    蛋(){}
    int _val=6;
    
    friend std::ostream&Operator您阅读了吗?还有。出于好奇,您为什么要避免通过引用传递记录?实现要求?可以说,您需要简单的可复制性,以便从字节数组复制到对象中(以恢复其值而不是UB)。如果对象是可复制的,则编译器有可能省略复制构造函数,即使它不属于复制省略异常。@Columbo我真的不知道。这取决于您对fine的意思。我肯定它需要处理可复制的类型。有人讨论过,可复制的对象也是可复制的这里很严格,我记不清所有细节。@dyp Pablo没有从Chandler那里听到任何关于磁盘旋转的消息,他的想法只是关于破坏性的移动优化。他说noop构造函数已经死了,他无意重提这个话题。至少
    std::aligned_storage
    it!@Yakk最新编辑地址对齐评论并添加了未调用构造函数的证据。这会导致未定义的行为-只能在C++@MattMcNabb中读取联合的最后一个写入成员,而严格阅读标准支持此想法,我认为您会发现,该行为是可预测的,因为联合中所有项的地址都是相同的,并且在最低公共对齐boundary@MattMcNabbCast
    std::addressof(_记录)
    char*
    并写入。我认为别名规则明确允许这样的操作?这真的很有趣。你为什么要让
    no_init_t
    像这样私下构造呢?@barry你应该使用
    no_init
    名称,而不是
    no_init_t{}
    。基本上,模仿
    nullptr\u t
    。我明白了-我的意思是为什么不像
    static constexpr no\u init\u t no\u init;
    ?@Barry,因为那样的话
    no\u init\u t{/code>会起作用。我在模仿
    nullptr\u t
    ,在
    nullptr\u t{/code>不起作用的地方,你必须使用
    nullptr
    @Yakk?(我还想知道,从技术上讲,返回一个未初始化的东西是否正确。)
    auto record = getNext<Record>();
    
    namespace {
      struct no_init_t {
        template<class T, class=std::enable_if_t<std::is_pod<T>{}>>
        operator T()const{
          T tmp;
          return tmp;
        }
        static no_init_t instance() { return {}; }
        no_init_t(no_init_t const&) = default;
      private:
        no_init_t() = default;
      };
      static const no_init = no_init_t::instance();
    }
    
    
    struct Foo {
      char buff[1000];
      size_t hash;
      Foo():Foo(""){}
      template<size_t N, class=std::enable_if_t< (N<=sizeof(buff)) >>
      Foo( char const(&in)[N] ) {
        // some "expensive" copy and hash
      }
      Foo(no_init_t) {} // no initialization!
    };
    struct Record {
      int x;
      Foo foo;
      Record()=default;
      Record(no_init_t):
        x(no_init), foo(no_init)
      {}
    };
    
    #include <iostream>
    #include <array>
    #include <algorithm>
    
    struct egg {
        egg(int i) : _val(i) {}
        egg() {}
        int _val = 6;    
        friend std::ostream& operator<<(std::ostream& os, const egg& e) {
            return os << e._val; 
        }
    };
    
    struct TestRecord {
        egg x;
        double y;
    };
    
    void populateNext(uint8_t* first, size_t length)
    {
        // do work here
        TestRecord data_source { 10, 100.2 };
        auto source = reinterpret_cast<uint8_t*>(&data_source);
        std::copy(source, source + length, first);
    }
    
    template<class Record>
    struct RecordProxy
    {
        RecordProxy() {}
    
      uint8_t* data() {
          return _data;
      }
    
      static constexpr size_t size() {
          return sizeof(Record);
      }
    
      Record& as_record() {
          return _record;
      }
    
        union {
            Record _record;
            uint8_t _data[sizeof(Record)];
        };
    };
    
    
    template <typename Record>
    RecordProxy<Record> getNext() {
        RecordProxy<Record> r;
        populateNext(r.data(),  // maybe ::read()
                     r.size());                   // or equivalent
        return r;
    }
    
    using namespace std;
    int main()
    {
        RecordProxy<TestRecord> prove_not_initialised;
        auto& r1 = prove_not_initialised.as_record();
        cout << "x = " << r1.x << ", y = " << r1.y << endl;
    
        auto buffer = getNext<TestRecord>();
        auto& actual_record = buffer.as_record();
        cout << "x = " << actual_record.x << ", y = " << actual_record.y << endl;
       return 0;
    }