[英]Why does C++ not have a const constructor?
( 编辑:重大更改,因为先前的示例有缺陷,这可能使某些答案/评论显得有些奇怪)
这可能是一个过分的设计,但是由于缺少const构造函数,因此以下内容合法:
class Cheater
{
public:
Cheater(int avalue)
: cheaterPtr(this) //conceptually odd legality in const Cheater ctor
, value(avalue)
{}
Cheater& getCheaterPtr() const {return *cheaterPtr;}
int value;
private:
Cheater * cheaterPtr;
};
int main()
{
const Cheater cheater(7); //Initialize the value to 7
cheater.value = 4; //good, illegal
cheater.getCheaterPtr().value = 4; //oops, legal
return 0;
}
看起来,提供const构造函数在技术上和const方法一样容易,并且类似于const重载。
注意:我不是在寻找' Image( const Data & data ) const
'而是' const Image( const Data & data) const
'
所以:
以下是一些有关上下文的材料:
仅仅因为Image
在您虚构的构造const
中为const
,并不意味着m_data
指向的是。 您最终将能够在类内部将“指向const的指针”分配给“指向非const的const指针”,这将删除constness而无需强制转换。 显然,这将允许您违反不变式,并且不允许这样做。
据我所知,可以在当前标准中准确,完整地指定所需的任何特定的常数集。
另一种看待它的方式是const
表示该方法不会改变对象的状态。 构造函数的唯一目的是将对象的状态初始化为有效(希望无论如何-应当仔细评估具有副作用的任何构造函数)。
编辑:在C ++中,常量性适用于两个成员,并且对于指针和引用,适用于所引用对象的可访问常量。 C ++自觉地决定将这两个不同的常量分开。 首先,我们是否同意该演示差异的代码应编译并打印出“非常量”?
#include <iostream>
struct Data
{
void non_const() { std::cout << "non-const" << std::endl; }
};
struct Image
{
Image( Data & data ) : m_data( data ) {}
void check() const { m_data.non_const(); }
Data & m_data;
};
int main()
{
Data data;
const Image img(data);
img.check();
return 0;
}
因此,为了获得可以接受const-ref并将其存储为const-ref的行为,必须将引用的有效声明更改为const。 然后,这将意味着它将是一个完全不同的类型,而不是原始类型的const
版本(因为在const中,成员类型不同的两种类型在C ++中被视为两种单独的类型)。 因此,编译器要么必须能够做大量的幕后魔术来来回转换这些东西,记住成员的常数,要么就必须将其视为一个单独的类型,而后又不能代替普通类型使用。
我认为您想要实现的是referencee_const对象,该概念仅在C ++中作为单独的类存在(我怀疑可以通过谨慎使用模板来实现,尽管我没有尝试过)。
严格来说,这是理论上的问题吗(答案:C ++决定拆分对象和引用常量),或者您要解决的是实际的实际人为问题?
它本身不是const方法
如果此构造方法本身不是const
方法,则内部指针等也将不是const
。 因此,它无法将const
值设置为这些非const
成员。
进行的唯一方式,语法,工作是这个构造要求成员初始化所有非mutable
成员。 本质上,使用此构造const
时,任何未声明为mutable
成员都将隐式声明为const
。 这相当于使构造函数成为const
方法; 只有初始化程序才能初始化成员。 构造函数的主体不能对非可变成员执行任何操作,因为在那一点上那些成员将是const
。
您要的是语法上可疑的。 本质上,您试图欺骗API,将常量数据存储在旨在用于可变数据的对象中(这就是为什么未将成员指针声明为const
)。 如果您希望对象具有不同的行为,则需要声明该对象具有该特定行为。
Mark B超越了基本考虑因素,但请注意,您可以在纯C ++中执行类似的操作。 考虑:
struct Data { };
class ConstImage {
protected:
const Data *const_data;
public:
ConstImage (const Data *cd) : const_data(cd) { }
int getFoo() const { return const_data->getFoo(); }
};
class Image : public ConstImage {
protected:
Data *data() { return const_cast<Data *>(const_data); }
public:
Image(Data *d) : const_data(d) { }
void frob() { data()->frob(); }
};
而不是使用const Image *
,而是使用ConstImage *
,然后就可以了。 您还可以简单地定义一个静态函数伪构造函数:
const Image *Image::newConstImage(const Data *d) {
return new Image(const_cast<Data*>(d));
}
当然,这依赖于程序员来确保不存在任何可能以某种方式使指向Data
的状态发生变化的const
函数。
您还可以结合使用以下技术:
class Image {
protected:
const Data *const_data;
Data *data() { return const_cast<Data *>(const_data); }
public:
void frob() { data()->frob(); }
int getFoo() const { return const_data->getFoo(); }
Image(Data *d) : const_data(d) { }
static const Image *newConst(const Data *cd) {
return new Image(const_cast<Data *>(cd));
}
};
这是两全其美的。 由于data()
是非常量成员,因此您需要对指向的值进行静态检查。 但是,您也具有const构造函数,并且可以直接在Image *
和const Image *
(即,如果知道安全性,则可以删除const Image *
)。
您还可以进一步抽象指针的分离:
template<typename T>
class ConstPropPointer {
private:
T *ptr;
public:
ConstPropPointer(T *ptr_) : ptr(ptr_) { }
T &operator*() { return *ptr; }
const T &operator*() const { return *ptr; }
T *operator->() { return ptr; }
const T *operator->() const { return ptr; }
};
class Image {
protected:
ConstPropPointer<Data> data;
public:
void frob() { data->frob(); }
int getFoo() const { return data->getFoo(); }
Image(Data *d) : data(d) { }
static const Image *newConst(const Data *cd) {
return new Image(const_cast<Data *>(cd));
}
};
现在,如果this
是const,则data
变为const,并将其也传播为*data
。 对你足够好吗? :)
我想最终的答案可能是这样的:为了使const构造函数有用且安全,我们需要在语言中内置类似ConstPropPointer
类的东西。 然后,将允许const构造函数将const T *
分配给constprop T *
。 这比听起来要复杂得多,例如,这如何与模板类(例如vector
相互作用?
因此,这是一个有点复杂的更改,但是问题似乎并没有解决的那么多。 更重要的是,这里有一个简单的解决方法( ConstPropPointer
可以被解放,而静态伪构造函数很容易添加)。 因此,C ++委员会可能甚至将其移交给更重要的事情,甚至根本没有提出。
在我看来,ctor没有返回类型说明这一事实在这里是失败的。 任何其他可以想象的语法,例如
class A
{
const A& ctor(...);
}
恕我直言,这将是非常有价值的。 例如,想象一下用原型调用方法的情况
void my_method(const my_own_string_class& z);
如果my_own_string_class拥有char *中的ctor,则编译器可以选择此ctor,但是由于不允许该ctor返回const对象,因此需要分配和复制...如果允许const返回类型,则可以执行此操作
class my_own_string_class
{
char *_ptr;
public:
const my_own_string_class& ctor(char *txt)
: _ptr(txt)
{ return *this;}
}
前提是该特殊构造仅限于时间实例的创建。 (并且dtor必须是可变的;))。
const对象应该初始化其成员变量,而const构造函数将无法这样做。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.