是否修改 const 对象的内部字节是未定义的行为,以防它包含另一个由 placement new 构造的对象?
Is modifying the internal bytes of a const object undefined behavior in case it contains another object constructed by placement new?
考虑以下示例:
#include <new>
struct FunctionObject
{
int operator()() // non-const, modifies the state of this object
{
i += 1;
j += 2;
return i + j;
}
int i = 0;
int j = 0;
};
struct Wrapper
{
explicit Wrapper(const FunctionObject& input_object)
{
constructed_object = ::new (buffer) FunctionObject(input_object);
}
~Wrapper()
{
constructed_object->~FunctionObject();
}
int operator()() const // const, but invokes the non-const operator() of the internal FunctionObject
{
return (*constructed_object)(); // this call modifies the internal bytes of this Wrapper
}
alignas(FunctionObject) unsigned char buffer[sizeof(FunctionObject)];
FunctionObject* constructed_object = nullptr;
};
int test()
{
const FunctionObject function_object{3, 4};
const Wrapper object_wrapper{function_object}; // this call modifies the internal bytes of a const Wrapper
return object_wrapper();
}
A Wrapper
包含一个内部 FunctionObject
,它是在 Wrapper
内部通过放置新构造的。
Wrapper
对象是const
,它的operator()
也是const
,但是调用它会导致对象的内部状态被修改。在很多情况下,类似的场景在C++中都是未定义的行为。
问题是,在这种特殊情况下是否存在未定义的行为(〜我需要将 buffer
标记为 mutable
吗?),或者 C++ 标准是否允许编写这样的代码?
这是未定义的行为。
来自 [dcl.type.cv],
Any attempt to modify a const object during its lifetime results in
undefined behavior.
将 mutable
说明符添加到 buffer
将允许它被 const
成员函数修改。
来自 [class.mfct.non-static.general/4]:
A non-static member function may be declared const
[...]. These cv-qualifiers affect the type of the this
pointer. They also affect the function type of the member function; a member function declared const
is a const member function [...].
然后从[class.this/1]:
The type of this
in a member function whose type has a cv-qualifier-seq cv and whose class is x
is "pointer to cv x". [Note 1: Thus in a const member function, the object for which the function is called is accessed through a const access path. -- end note]
最终来自 [dcl.type.cv/3-4]:
[...] a const-qualified access path cannot be used to modify an object [...]
Any attempt to modify a const object during its lifetime results in undefined behavior.
因此,您的 test()
例程显示未定义的行为,即使您的 Wrapper 对象 object_wrapper
不是 const
。
没有。它不会是实践中未定义的行为,忘记“标准”所描述的内容。它的行为就像没有“const”一样。
原因是 C++ 中对象的“const”限定符只是告诉编译器在调用对象内部的某些成员或函数时产生编译错误。除此之外,向对象添加“const”限定符不会在编译后产生不同的二进制文件。
这不同于应用于原始类型的“const”限定符,例如“const int”、“const char *”。出于优化目的,“const int”类型的变量可能会被其 compile-time 值替换。类型为“const char *”的字符串文字将引用某些内存页,该内存页受到 OS 的读取访问限制,修改内存内容将导致程序崩溃。
此外,函数的“const”限定符确实会导致不同的函数签名,并且可以像函数重载一样对待。
考虑以下示例:
#include <new>
struct FunctionObject
{
int operator()() // non-const, modifies the state of this object
{
i += 1;
j += 2;
return i + j;
}
int i = 0;
int j = 0;
};
struct Wrapper
{
explicit Wrapper(const FunctionObject& input_object)
{
constructed_object = ::new (buffer) FunctionObject(input_object);
}
~Wrapper()
{
constructed_object->~FunctionObject();
}
int operator()() const // const, but invokes the non-const operator() of the internal FunctionObject
{
return (*constructed_object)(); // this call modifies the internal bytes of this Wrapper
}
alignas(FunctionObject) unsigned char buffer[sizeof(FunctionObject)];
FunctionObject* constructed_object = nullptr;
};
int test()
{
const FunctionObject function_object{3, 4};
const Wrapper object_wrapper{function_object}; // this call modifies the internal bytes of a const Wrapper
return object_wrapper();
}
A Wrapper
包含一个内部 FunctionObject
,它是在 Wrapper
内部通过放置新构造的。
Wrapper
对象是const
,它的operator()
也是const
,但是调用它会导致对象的内部状态被修改。在很多情况下,类似的场景在C++中都是未定义的行为。
问题是,在这种特殊情况下是否存在未定义的行为(〜我需要将 buffer
标记为 mutable
吗?),或者 C++ 标准是否允许编写这样的代码?
这是未定义的行为。
来自 [dcl.type.cv],
Any attempt to modify a const object during its lifetime results in undefined behavior.
将 mutable
说明符添加到 buffer
将允许它被 const
成员函数修改。
来自 [class.mfct.non-static.general/4]:
A non-static member function may be declared
const
[...]. These cv-qualifiers affect the type of thethis
pointer. They also affect the function type of the member function; a member function declaredconst
is a const member function [...].
然后从[class.this/1]:
The type of
this
in a member function whose type has a cv-qualifier-seq cv and whose class isx
is "pointer to cv x". [Note 1: Thus in a const member function, the object for which the function is called is accessed through a const access path. -- end note]
最终来自 [dcl.type.cv/3-4]:
[...] a const-qualified access path cannot be used to modify an object [...]
Any attempt to modify a const object during its lifetime results in undefined behavior.
因此,您的 test()
例程显示未定义的行为,即使您的 Wrapper 对象 object_wrapper
不是 const
。
没有。它不会是实践中未定义的行为,忘记“标准”所描述的内容。它的行为就像没有“const”一样。
原因是 C++ 中对象的“const”限定符只是告诉编译器在调用对象内部的某些成员或函数时产生编译错误。除此之外,向对象添加“const”限定符不会在编译后产生不同的二进制文件。
这不同于应用于原始类型的“const”限定符,例如“const int”、“const char *”。出于优化目的,“const int”类型的变量可能会被其 compile-time 值替换。类型为“const char *”的字符串文字将引用某些内存页,该内存页受到 OS 的读取访问限制,修改内存内容将导致程序崩溃。
此外,函数的“const”限定符确实会导致不同的函数签名,并且可以像函数重载一样对待。