将const移出具有保证的非const分配的非静态const字段合法吗

问题描述

我有以下似乎总是可以运行的代码(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);
}