今天我遇到了一些大致类似于以下代码段的代码.二者valgrind并UndefinedBehaviorSanitizer检测读取未初始化的数据.
template <typename T>
void foo(const T& x)
{
static_assert(std::is_pod_v<T> && sizeof(T) > 1);
auto p = reinterpret_cast<const char*>(&x);
std::size_t i = 1;
for(; i < sizeof(T); ++i)
{
if(p[i] != p[0]) { break; }
}
// ...
}
Run Code Online (Sandbox Code Playgroud)
p[i] != p[0]当传递包含填充字节的对象时,上述工具抱怨比较foo.例:
struct obj { char c; int* i; };
foo(obj{'b', nullptr});
Run Code Online (Sandbox Code Playgroud)
从POD类型中读取填充字节并将它们与其他内容进行比较是不确定的行为?我无法在Standard和StackOverflow中找到明确的答案.
c++ padding undefined-behavior language-lawyer standard-layout
看看这段代码:
struct A {
short s;
int i;
};
struct B {
short s;
int i;
};
union U {
A a;
B b;
};
int fn() {
U u;
u.a.i = 1;
return u.b.i;
}
Run Code Online (Sandbox Code Playgroud)
是否保证fn()退货1?
注:这是一个后续问题要这个.
我需要写一些整数类型的单个字节.我应该使用reinterpret_cast,还是应该使用static_castvia void*?
(一个)
unsigned short v16;
char* p = static_cast<char*>(static_cast<void*>(&v16));
p[1] = ... some char value
p[0] = ... some char value
Run Code Online (Sandbox Code Playgroud)
或(b)
unsigned short v16;
char* p = reinterpret_cast<char*>(&v16);
p[1] = ... some char value
p[0] = ... some char value
Run Code Online (Sandbox Code Playgroud)
根据static_cast和reinterpret_cast对std :: aligned_storage的回答都应该是等价的 -
- 如果T1和T2都是标准布局类型,并且T2的对齐要求不比T1更严格
我倾向于reinterpret_cast为是基本上我在做什么,不是吗?
还有其他需要考虑的事情,特别是我们正在编译的Visual-C++和VC8版本吗?(x86只有atm.)
我正在编写一个类,假设枚举类型布局的答案与其基础类型兼容?是"是",是布局兼容struct kevent,但使用enum class的ES filter,flags等有合适的标的类型相关的字段.它也是标准布局(字段全部private都是标准布局,没有virtual成员,没有基类).从我的阅读中n3690,我可以确定我的班级并且struct kevent具有相同的价值表示,但我无法看到标准中的任何内容因此允许我reinterpret_cast介于它们之间,即使这似乎是对"价值表征"的合理解释.这在技术上是否符合标准?如果没有,知道一个类型的值表示给你什么?
编辑2014/02/24 16:45 EST:在回复评论时,我应该澄清我想要reinterpret_cast第一个类来引用第二个,当然你不能直接reinterpret_cast将非指针类型转换为另一个非 - 指针类型.
根据C++标准:
标准布局类是一个类:
- 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员.
引用的哪些属性会阻止具有引用成员的类包含在标准布局类的定义中?
C++标准指定mutex,atomics或conditinal_variable是标准布局类型.
这个规范有什么好处?用户如何利用此属性?
总的来说,如果知道一个类型是标准布局而不知道其实现的细节,我可以获得什么?
C++ 11标准保证逐字节副本始终对POD类型有效.但是某些琐碎的类型呢?
这是一个例子:
struct trivial
{
int x;
int y;
trivial(int i) : x(2 * i) { std::cout << "Constructed." << std::endl; }
};
Run Code Online (Sandbox Code Playgroud)
如果我要复制这个结构,逐字节,是否保证正确复制,即使它在技术上不是POD?是什么时候绘制的是什么时候不能对对象进行字节复制?
我将一个简单的C++继承层次结构包装成"面向对象的"C.我试图弄清楚是否有任何问题将C++对象的指针视为指向不透明C结构的指针.特别是,在什么情况下派生到基础的转换会导致问题?
类本身相对复杂,但层次结构很浅并且仅使用单继承:
// A base class with lots of important shared functionality
class Base {
public:
virtual void someOperation();
// More operations...
private:
// Data...
};
// One of several derived classes
class FirstDerived: public Base {
public:
virtual void someOperation();
// More operations...
private:
// More data...
};
// More derived classes of Base..
Run Code Online (Sandbox Code Playgroud)
我计划通过以下相当标准的面向对象的C向C客户端展示它:
// An opaque pointers to the types
typedef struct base_t base_t;
typedef struct first_derived_t first_derived_t;
void base_some_operation(base_t* object) {
Base* base = (Base*) object; …Run Code Online (Sandbox Code Playgroud) 我发现一些线程严重意味着无法完成,但没有一个使用完全相同的运算符和条件组合,所以我想更具体地问.希望这意味着对某人来说这是一个快速而简单的答案......无论如何!
考虑一个示例代理类,用于管理较大存储块中的值 - 如此过于简化但有代表性的示例:
class SomeProxyThing {
std::uint32_t storage;
public:
operator std::uint16_t() const
{
return storage & 0x0000FFFF;
}
SomeProxyThing &operator=(std::uint16_t const value)
{
storage &= 0xFFFF0000;
storage |= value;
}
};
Run Code Online (Sandbox Code Playgroud)
我想通过用户定义的s 来完成所有作业operator.在这种情况下,用户应该只能传入或取出"暴露"类型std::uint16_t.我可能正在使用各种代理类类型,并希望它适用于所有这些类型.理想情况下,对于任何类型的组合,我只需键入someProxy = anotherProxy并让编译器完成其余的工作.
但是,当赋值的左侧和右侧具有相同或继承相关的类型时,默认的复制赋值运算符 - 当然 - 与此目标冲突.它复制了整个storage,从而破坏了另一半uint32_t- 而不是根据需要只复制'暴露'值.这是正确的!对于大多数情况.但即使LHS和RHS类型相同,我也想要一种"通过转换分配"的方法.为了避免这种情况,我可以:
operators 执行'代理'副本- 这是我一直在做的,但它似乎有点hacky,并且像任何用户定义的构造函数/赋值运算符一样,打破了平凡的可复制状态对的struct-我需要保持.它仍然memcpy()s ^ 反正在g++,但我想定义的行为.= delete复制赋值运算符(我们现在可以为TC类型执行).但是,赋值仍然会尝试使用它并抛出编译错误 - …一段代码值得千言万语.
#include <iostream>
#include <type_traits>
using namespace std;
struct A
{
int a;
};
struct B : A
{
int b;
};
int main()
{
cout << is_standard_layout<B>::value << endl; // output false! WHY?
return 0;
}
Run Code Online (Sandbox Code Playgroud) 首先,这更像是一个健全性检查问题,以获得一些比我更熟悉语言标准深度的人的认可.
假设我有以下类型(虽然我省略了任何非构造函数和非赋值成员函数):
template<typename E> struct half_expr
{
};
class half : public half_expr<half>
{
public:
half();
explicit half(float);
template<typename E> half(const half_expr<E>&);
half& operator=(float);
template<typename E> half& operator=(const half_expr<E>&);
private:
half(std::uint16_t, bool);
std::uint16_t data_;
};
Run Code Online (Sandbox Code Playgroud)
好吧,在任何合理的实现上half,内存中的其他东西不应该是普通的std::uint16_t.但我对标准的保证感兴趣.根据POD的C++ 98/03定义,这是我的理由:
half 不能是POD类型,因为它具有非公共字段,基类和用户定义的构造函数.和C++ 11失败/扩展定义:
它应该是可以轻易复制的,因为它只隐式生成了复制/移动构造函数/赋值,但只要这些float和模板版本不以任何方式计数,我不完全确定.
它也应该是标准布局,因为它只有一个基本类型的私有字段和一个空的非虚拟基类(在任何标准中都应该是POD,对吗?)
阻碍POD分类的唯一因素是它不是简单的默认构造,可以通过使用C++ 11的half() = default语法来克服IMHO .
我的一个非常简单的问题是:我的理由是完全正确还是在定义中有任何我忽略或误解的东西,特别是考虑到用户定义的构造函数和赋值会以某种方式阻碍分类的简单复制?
注意:即使你觉得有必要将这一点委托给一些关于POD和标准布局类型的可能重复(我可以完全理解),回答我实际问题的简单评论仍然会很好,因为这只是一个简单的检查,这可能对你来说简单或过分,但我只想保持安全.
在 C++ 中,将指向标准布局类型的指针reinterpret_cast指向指向 的任何成员的指针是合法的,可能使用宏。如果只有一名成员,则没有必要,这是合法的: SSoffsetofSoffsetof
struct S {
int x;
};
static_assert(std::is_standard_layout_v<S>);
void f(S s) {
// this is legal for standard-layout classes, offsetof the first member is zero
S *s0 = &s;
int *x0 = reinterpret_cast<int *>(s0);
}
Run Code Online (Sandbox Code Playgroud)
但现在假设我有一个数组S:
struct S {
int x;
};
static_assert(std::is_standard_layout_v<S>);
void f(S s) {
// this is legal for standard-layout classes, offsetof the first member is zero
S *s0 = &s;
int …Run Code Online (Sandbox Code Playgroud) 我读了一篇关于POD,Trivial,Standard-layout类的非常好的文章.但我对标准布局类的规则有疑问:
要么在大多数派生类中没有非静态数据成员,要么最多只有一个具有非静态数据成员的基类,或者没有带有非静态数据成员的基类
我写了一个源代码:
#include <iostream>
struct A {
int a;
};
struct B {
int b;
};
struct C : A, B {
int c;
};
int main() {
C c = {}; // initialize C
c.a = 0xffffffff;
c.b = 0xeeeeeeee;
c.c = 0xdddddddd;
std::cout << std::hex << *(reinterpret_cast<int*>(&c) ) << std::endl;
std::cout << std::hex << *(reinterpret_cast<int*>(&c)+1) << std::endl;
std::cout << std::hex << *(reinterpret_cast<int*>(&c)+2) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
结果是:
ffffffff
eeeeeeee
dddddddd
Run Code Online (Sandbox Code Playgroud)
我觉得它运作得很好.在VS2015中使用调试器,它看起来很好.
那么,为什么在继承的标准布局规则中有非静态成员的限制?