请考虑以下代码。
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
类型不能分配给非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;
}
为什么在使用模板时会出现问题?
答案 0 :(得分:6)
因为你有两个间接层 - 在你的main函数中,对value
的调用会返回对非const foo
的const指针的引用。
这可以安全地复制到非const指针中,指向非const foo
。
如果您使用test
实例化const foo *
,那将是另一回事。
const test<const foo*> t;
foo* f = t.value(); // error
const foo* f = t.value(); // fine
return 0;
<强>更新强>
来自评论:
value()返回const T&amp;哪个行 只被分配给另一个const 类型。但在这种情况下,编译器是 安全地允许转换。
只能读取Const数据。它不能写(“变异”)。但是复制一些数据是一种阅读方式,所以没关系。例如:
const int c = 5;
int n = c;
这里,我在c
中有一些const数据,我将数据复制到非const变量n中。没关系,它只是读取数据。 c
中的值尚未修改。
现在,假设您的foo
中包含一些数据:
struct foo { int n; };
如果我有一个指向其中一个的非const指针,我可以通过指针修改n
值。您要求test
模板存储指向非const foo
的指针,然后创建test
的const实例。因此,只有指针地址是常量。没有人可以更改test
内指针中存储的地址,因此无法指向另一个对象。但是,它指向的对象可以修改其内容。
更新2:
当您制作示例的非模板版本时,您犯了一个错误。要做到正确,您需要将foo *
替换为T
的每个地方。
const T& value() const
请注意,您在那里引用了const T
。所以返回值将是对const:a foo *
的引用。它只是指针地址无法修改。它指向的对象可以修改其内容。
在你的第二个例子中,你摆脱了引用部分,它改变了含义并使const
修饰符适用于指针指向的对象,而不是应用于指针本身。
答案 1 :(得分:1)
使用以下模板专业化:
template<typename T>
class test<T*>
{
public:
test() {}
const T* value() const
{
return f;
}
private:
T* f;
};
包含这个之后,g ++说:
d.cpp: In function ‘int main()’:
d.cpp:41: error: invalid conversion from ‘const foo*’ to ‘foo*’
答案 2 :(得分:1)
你的代码没有任何问题,对指针的const引用只意味着你不能修改指针,但指向的对象仍然是完全可变的。如果您在main
函数中尝试更改f
t
成员指向的地址,您将看到不能:封装完全保留。
这与使以下代码有效的原理相同:
void foo(std::vector<int *> const & v)
{
*v[0] = 0; // op. [] returns const & to int *
}
对C ++不熟悉的人通常会对这种行为感到惊讶,因为对于他们来说,const向量不应该允许修改其元素。事实上它没有,因为存储在向量中的指针不会改变(它一直指向同一个地址)。它是被修改的指向对象,但是向量并不关心它。
唯一的解决方案就是像Amit所说的那样,并为T*
提供班级的专业化。