问题描述
我有以下似乎总是可以运行的代码(msvc,gcc和clang)。
但是我不确定这是否真的合法。在我的框架中,我的类可能具有“两个构造函数”-一个执行简单成员初始化的普通C ++构造函数和一个执行附加初始化代码的附加成员函数“ Ctor”。例如,它用于允许调用虚拟函数。这些调用由通用分配/构造函数处理-类似于“ make_shared”。
代码:
#include <iostream>
class Foo
{
public:
constexpr Foo() : someConstField(){}
public:
inline void Ctor(int i)
{
//use Ctor as real constructor to allow for example calls to virtual functions
const_cast<int&>(this->someConstField) = i;
}
public:
const int someConstField;
};
int main()
{
//done by a generic allocation function
Foo f;
f.Ctor(12); //after this call someConstField is really const!
//
std::cout << f.someConstField;
}
解决方法
修改const
内存是未定义的行为。此处int
已由默认构造函数分配在const内存中。
老实说,我不确定您为什么首先要这样做。如果您想使用Foo
来初始化int
,只需创建一个重载的构造函数即可:
...
constexpr Foo(int i) : someConstField{i} {}
这是完全合法的,您在创建const
内存时就启动了它,一切都很好。
如果出于某种原因要分两个阶段初始化对象(没有工厂功能就不是一个好主意),则不能(也不应)使用{{1} }成员变量。毕竟,如果在创建对象之后它可以更改,那么它将不再是const。
一般来说,您不应该拥有const
成员变量,因为它会引起很多问题,例如移动对象。
当我在这里说“常量内存”时,我的意思是按照语言规则const
个合格的内存。因此,尽管内存本身在计算机级别上可能是可写的,但实际上并不重要,因为编译器将执行其喜欢的任何操作(通常,它只忽略对该内存的任何写操作,但这是UB,因此它实际上可以执行任何操作)。
否。
修改const
值是未定义的行为。 const_cast
本身很好,是问题所在。
根据C ++ 17标准中的7.1.6.1
除了可以声明任何声明为可变的类成员(7.1.1)之外,任何尝试修改const的尝试 对象在其生命周期(3.8)中会导致未定义的行为。
有一个示例(与您的示例类似,但类成员除外):
funcA() {
service.testEmitter.subscribe()....//
}
,
我建议您使用构造函数来避免const强制转换。您评论说,在调用Ctor
之后,someConstField
的值将保持不变。只需在构造函数中进行设置,就不会有任何问题,并且代码将变得更具可读性。
#include <iostream>
class Foo
{
public:
constexpr Foo(int i) : someConstField(Ctor(i)){}
int Ctor(); // to be defined in the implementation
const int someConstField;
};
int main()
{
Foo f(12);
std::cout << f.someConstField;
}
,
如果分配函数分配了原始内存,则可以使用placement new
在该内存位置构造一个对象。这样,您必须记住在释放分配之前调用对象的析构函数。
使用malloc
的小示例:
class Foo
{
public:
constexpr Foo(int i) : someConstField(i){}
public:
const int someConstField;
};
int main()
{
void *raw_memory = std::malloc(sizeof(Foo));
Foo *foo = new (raw_memory) Foo{3}; // foo->someConstField == 3
// ...
foo->~Foo();
std::free(foo);
}