(编辑:因为上一个例子存在缺陷而发生重大变化,这可能会使一些答案/评论看起来很奇怪)
这可能过于人为,但由于缺少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
'
所以:
以下是上下文的一些相关材料:
答案 0 :(得分:10)
仅仅因为Image
在你想象的构造函数中是const
并不意味着m_data
指向的是什么。您最终能够将“指向const的指针”分配给类中的“const指向非const”,这样可以在没有强制转换的情况下删除const。这显然会让你违反不变量而且不允许。
据我所知,在当前标准范围内,可以准确,完整地指定所需的任何特定常数。
另一种看待它的方法是const
表示该方法不会改变对象的状态。构造函数的唯一目的是初始化一个对象的状态是有效的(无论如何 - 希望任何具有副作用的构造函数都应该......仔细评估)。
#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 ++决定拆分对象和引用常量)还是有一个实际的实际未解决的问题,你试图解决?
答案 1 :(得分:5)
它本身不是const方法
如果此构造函数本身不是const
方法,那么内部指针等也不会是const
。因此,它无法将const
值设置为非const
成员。
使其在语法上有效的唯一方法是,此构造函数需要对所有非mutable
成员进行成员初始化。从本质上讲,使用此构造函数时,任何未声明为mutable
的成员都将被隐式声明为const
。这相当于使构造函数成为const
方法;只有初始化程序才能初始化成员。构造函数的主体无法对不可变成员执行任何操作,因为那时成员将是const
。
你要求的是语法上可疑的。您实际上是在试图欺骗API,将常量数据存储在为可变数据设计的对象中(这就是为什么您没有将成员指针声明为const
)。如果您想要对象的不同行为,则需要声明该对象具有该特定行为。
答案 2 :(得分:4)
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));
}
当然,这取决于程序员确保没有任何const
函数可能以某种方式改变指向Data
的状态。
您还可以结合使用这些技术:
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成员,因此您可以静态检查指向值的变异。但是,你也有一个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 ++委员会可能会将其传递给更重要的事情,如果它甚至提出的话。
答案 3 :(得分:0)
在我看来,ctors没有返回型规格的事实就是这里失败的。任何其他可以想象的语法,例如
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必须是可变的;))。
答案 4 :(得分:-1)
Const对象应该初始化它们的成员变量,而const构造函数则不能这样做。