const 正确性和返回值 - C++
请考虑以下代码。
struct foo
{
};
template<typename T>
class test
{
public:
test() {}
const T& value() const
{
return f;
}
private:
T f;
};
int main()
{
const test<foo*> t;
foo* f = t.value();
return 0;
}
t
是一个 const
变量,value()
是一个常量成员函数,它返回 const T&
。 AFAIK,const 类型不能分配给非常量类型。但是 foo* f = t.value();
如何编译良好。这是如何发生的以及如何确保 value()
只能分配给 const foo*
?
编辑
我发现,这种情况发生在使用模板时。以下代码按预期工作。
class test
{
public:
test() {}
const foo* value() const { return f; }
private:
foo* f;
};
int main()
{
const test t;
foo* f = t.value(); // error here
return 0;
}
为什么使用模板时会出现问题?
Please consider the following code.
struct foo
{
};
template<typename T>
class test
{
public:
test() {}
const T& value() const
{
return f;
}
private:
T f;
};
int main()
{
const test<foo*> t;
foo* f = t.value();
return 0;
}
t
is a const
variable and value()
is a constant member-function which returns const T&
. AFAIK, a const
type is not assignable to a non-const type. But how foo* f = t.value();
compiles well. How this is happening and how can I ensure value()
can be only assigned to const foo*
?
Edit
I found that, this is happening on when templates are used. Following code works as expected.
class test
{
public:
test() {}
const foo* value() const { return f; }
private:
foo* f;
};
int main()
{
const test t;
foo* f = t.value(); // error here
return 0;
}
Why the problem is happening when templates are used?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
因为您有两个间接级别 - 在主函数中,对 value 的调用返回对指向非 const foo 的 const 指针的引用。
这可以安全地复制到指向非常量
foo
的非常量指针中。如果您使用
const foo *
实例化test
,那么情况就会不同。更新
来自评论:
const 数据只能读取。它无法被写入(“变异”)。不过复制一些数据也是读取的一种方式,所以也没关系。例如:
这里,我在
c
中有一些const数据,我将这些数据复制到一个非常量变量n中。没关系,只是读取数据而已。c
中的值尚未修改。现在,假设您的
foo
中有一些数据:如果我有一个指向其中之一的非常量指针,我可以通过该指针修改
n
值。您要求test
模板存储指向非常量foo
的指针,然后创建test
的 const 实例。因此,只有指针地址是恒定的。没有人可以更改test
内部指针中存储的地址,因此无法使其指向另一个对象。但是,它指向的对象可以修改其内容。更新 2:
当您制作该示例的非模板版本时,您犯了一个错误。为了得到正确的结果,您需要将
foo *
替换为每个有T
的地方。请注意,您在那里有一个对 const
T
的引用。因此返回值将是对 const 的引用:afoo *
。只是指针地址不能修改。它指向的对象可以修改其内容。在第二个示例中,您删除了引用部分,这改变了含义并使 const 修饰符应用于指针指向的对象,而不是应用于指针本身。
Because you have two levels of indirection - in your main function, that call to
value
returns a reference to a const pointer to a non-constfoo
.This can safely be copied into non-const pointer to a non-const
foo
.If you'd instantiated
test
withconst foo *
, it would be a different story.Update
From the comment:
Const data can only be read. It cannot be written ("mutated"). But copying some data is a way of reading it, so it's okay. For example:
Here, I had some const data in
c
, and I copied the data into a non-const variable n. That's fine, it's just reading the data. The value inc
has not been modified.Now, suppose your
foo
had some data in it:If I have a non-const pointer to one of those, I can modify the
n
value through the pointer. You asked yourtest
template to store a pointer to a non-constfoo
, and then made a const instance oftest
. Only the pointer address is constant, therefore. No one can change the address stored in the pointer insidetest
, so it cannot be made to point to another object. However, the object it points to can have its contents modified.Update 2:
When you made your non-template version of the example, you made a mistake. To get it right, you need to substitute
foo *
into each place where there's aT
.Notice that you have a reference to a const
T
there. So the return value will be a reference to something const: afoo *
. It's only the pointer address that can't be modified. The object it points to can have its contents modified.In your second example, you got rid of the reference part, which changes the meaning and makes the
const
modifier apply to the object that the pointer points to, instead of applying to the pointer itself.使用以下模板专门化:
包含此后,g++ 会说:
Use the following template specialization:
After including this, g++ says:
您的代码中没有任何问题,对指针进行常量引用仅意味着您无法修改指针,但指向的对象仍然完全可变。如果在您的
main
函数中尝试更改t
的f
成员所指向的地址,您会发现不能:封装保存完好。这与使以下代码有效的原理相同:
刚接触 C++ 的人通常会对这种行为感到惊讶,因为对他们来说 const 向量不应该允许修改其元素。事实上它不会,因为存储在向量中的指针不会改变(它保持指向相同的地址)。它是被修改的指向对象,但向量并不关心它。
唯一的解决方案是按照 Amit 所说的那样,为
T*
提供类的专门化。There's nothing wrong in your code, having a const reference to a pointer only means that you can't modify the pointer, but the pointed-to object remains perfectly mutable. If inside your
main
function you try to change the address pointed to by thef
member oft
you'll see that you can't: encapsulation is perfectly preserved.This is the same principle that makes the following code valid:
People new to C++ are usually surprised by this behavior, because for them a const vector should not allow the modification of its elements. And in fact it doesn't, because the pointer stored in the vector does not change (it keeps pointing to the same address). It's the pointed-to object which is modified, but the vector does not care about that.
The only solution is to do as Amit says and provide a specialization of your class for
T*
.