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)
然而,在每个成员上构建fromno_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@MattMcNabbCaststd::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;
}