memcpy() 可以用于更改 "const" 成员数据吗?
can memcpy() be used to change "const" member data?
struct
有 const
个成员
struct point { const int x; const int y; };
用作会员数据
struct Foo
{
point pt{ 0, 0 };
void move_x(int value);
};
如何写Foo::move_x()
来更新Foo::pt
?使用 memcpy()
可以吗?
#include <memory.h>
void Foo::move_x(int value)
{
const point pt_{ pt.x + value, pt.y };
(void) memcpy(&pt, &pt_, sizeof(pt_)); // pt = pt_;
}
这可以使用指针安全地完成
#include <memory>
struct Bar
{
std::unique_ptr<point> pPt_{ new point{ 0, 0 } };
const point& pt() const {
return *pPt_;
}
void move_x(int value) {
pPt_.reset(new point{ pt().x + value, pt().y });
}
};
但是 point
总是存储在堆上而不是 Bar
。
请注意,客户根本不关心 point
:
Foo foo;
foo.move_x(314); // (314, 0)
Bar bar;
bar.move_x(3141); // (3141, 0)
这显然是未定义的行为。现在,"can" 这能完成吗?当然可以,但前提是编译器不会抱怨。但是 C++ 的一个方面只是因为编译器没有报错并不意味着生成的代码可以正常工作。
迟早会有人写出读取 pt
、调用 move_x()
、然后再次读取 pt
的代码。
现代优化编译器会正确地假设,因为代码正在读取 const
个实例,所以它们的值不能改变,然后使用数据继续并优化来自 pt
的第二次读取从 pt
的第一次读取缓存在 CPU 寄存器中。
然后,倒霉的程序员会花一个星期的时间试图弄清楚为什么代码明显没有按照程序所说的去做。
您将在尝试将 memcpy
与 const
成员一起使用时遇到编译器错误,如下所示:
#include <cstdlib>
#include <cstdio>
#include <cstring>
struct foo
{
public:
foo(int init_x);
const int x;
int bar();
};
foo::foo(int init_x): x(init_x) {}
int foo::bar()
{
int *y = (int *) malloc(sizeof(int));
*y = 6;
memcpy(&x, y, sizeof(int));
return x;
}
int main(int argc, char *argv[])
{
foo f{5};
printf("%d\n", f.bar());
}
以上结果
error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]
memcpy(&x, y, sizeof(int));
虽然我在这个例子中使用了 const int
,但是如果你使用 const int
指针成员,你会发现相同的结果,即
const int *x;
但是,如果您删除 const
描述符并使用:
int x;
(或 int *x;
,就此而言)错误不再发生,并且程序打印 6
,正如人们所期望的那样。
所以这引出了一个问题,如果你知道某些东西将被声明为 const
:
- 如果您有能力更改声明,您不能自己删除
const
吗?
- 如果您不能更改声明,您是否有充分的理由想要打破
const
造成的 "promise"?
struct
有 const
个成员
struct point { const int x; const int y; };
用作会员数据
struct Foo
{
point pt{ 0, 0 };
void move_x(int value);
};
如何写Foo::move_x()
来更新Foo::pt
?使用 memcpy()
可以吗?
#include <memory.h>
void Foo::move_x(int value)
{
const point pt_{ pt.x + value, pt.y };
(void) memcpy(&pt, &pt_, sizeof(pt_)); // pt = pt_;
}
这可以使用指针安全地完成
#include <memory>
struct Bar
{
std::unique_ptr<point> pPt_{ new point{ 0, 0 } };
const point& pt() const {
return *pPt_;
}
void move_x(int value) {
pPt_.reset(new point{ pt().x + value, pt().y });
}
};
但是 point
总是存储在堆上而不是 Bar
。
请注意,客户根本不关心 point
:
Foo foo;
foo.move_x(314); // (314, 0)
Bar bar;
bar.move_x(3141); // (3141, 0)
这显然是未定义的行为。现在,"can" 这能完成吗?当然可以,但前提是编译器不会抱怨。但是 C++ 的一个方面只是因为编译器没有报错并不意味着生成的代码可以正常工作。
迟早会有人写出读取 pt
、调用 move_x()
、然后再次读取 pt
的代码。
现代优化编译器会正确地假设,因为代码正在读取 const
个实例,所以它们的值不能改变,然后使用数据继续并优化来自 pt
的第二次读取从 pt
的第一次读取缓存在 CPU 寄存器中。
然后,倒霉的程序员会花一个星期的时间试图弄清楚为什么代码明显没有按照程序所说的去做。
您将在尝试将 memcpy
与 const
成员一起使用时遇到编译器错误,如下所示:
#include <cstdlib>
#include <cstdio>
#include <cstring>
struct foo
{
public:
foo(int init_x);
const int x;
int bar();
};
foo::foo(int init_x): x(init_x) {}
int foo::bar()
{
int *y = (int *) malloc(sizeof(int));
*y = 6;
memcpy(&x, y, sizeof(int));
return x;
}
int main(int argc, char *argv[])
{
foo f{5};
printf("%d\n", f.bar());
}
以上结果
error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]
memcpy(&x, y, sizeof(int));
虽然我在这个例子中使用了 const int
,但是如果你使用 const int
指针成员,你会发现相同的结果,即
const int *x;
但是,如果您删除 const
描述符并使用:
int x;
(或 int *x;
,就此而言)错误不再发生,并且程序打印 6
,正如人们所期望的那样。
所以这引出了一个问题,如果你知道某些东西将被声明为 const
:
- 如果您有能力更改声明,您不能自己删除
const
吗? - 如果您不能更改声明,您是否有充分的理由想要打破
const
造成的 "promise"?