【发布时间】:2025-12-20 00:40:11
【问题描述】:
我认为这个问题相当普遍,所以应该有一个已知的解决方案。我想出了一个,但我不是很满意,所以我在这里问,希望有人能帮忙。
假设我有一个函数,其签名是
template<typename T>
void foo(const MyArray<const T>& x);
模板参数中的 const 是为了防止我更改数组内容,因为(出于此问题之外的原因),MyArray<T> 的访问器([] 和 ())始终标记为 const,并返回对T 的引用(因此,const 确保安全,因为MyArray<T>::operator[] 返回T&,而MyArray<const T>::operator[] 返回const T&)。
太好了。但是,具有不同模板参数的模板是不相关的,所以我不能将 MyClass<T> 的引用绑定到 MyClass<const T> 的引用,这意味着我不能这样做
MyArray<double> ar(/*blah*/);
foo(ar);
请注意,如果没有引用,上面的代码将可以工作如果有一个复制构造函数可以让我从MyArray<T> 创建MyArray<const T>。但是,我不想删除引用,因为数组构造会发生很多次,而且尽管相对便宜,但它的成本会增加。
所以问题是:我如何用MyArray<T> 调用foo?
到目前为止,我唯一的解决方案如下:
MyArray<T> ar(/*blah*/);
foo(reinterpret_cast<MyArray<const T>>(ar));
(实际上在我的代码中,我将重新解释转换隐藏在具有更详细名称的内联函数中,但最终游戏是相同的)。 MyArray 类没有对 const T 的专门化,这使得它无法重新解释,因此演员表应该是“安全的”。但这并不是一个很好的阅读解决方案。另一种方法是复制foo 的签名,以获得采用MyArray<T> 的版本,该实现执行转换并调用const 版本。这个问题是代码重复(我有很多函数foo需要重复)。
也许foo 的签名上有一些额外的模板魔法?目标是同时传递 MyArray<T> 和 MyArray<const T>,同时仍保持 const 正确性(即,如果我不小心更改了函数体中的输入,则使编译器崩溃)。
编辑 1:MyArray 类(其实现不受我控制)具有 const 访问器,因为它存储指针。所以调用v[3] 将修改数组中的值,但不会修改存储在类中的成员(即指针和一些类似智能指针的元数据)。换句话说,对象实际上没有被访问器修改,尽管数组是。这是语义上的区别。不知道他们为什么要朝这个方向发展(我有一个想法,但解释的时间太长了)。
编辑 2:我接受了两个答案之一(尽管它们有些相似)。我不确定(出于长时间解释的原因)包装类在我的情况下是否可行(也许,我必须考虑一下)。我也很困惑,虽然
template<typename T>
void foo(const MyArray<const T>& x);
MyArray<int> a;
foo(a);
不编译,下面可以
void foo(const MyArray<const int>& x);
MyArray<int> a;
foo(a);
注意:MyArray 确实提供了带有签名的模板化“复制构造函数”
template<typename S>
MyArray(const MyArray<S>&);
所以它可以从MyArray<T> 创建MyArray<const T>。我很困惑为什么当T 是显式时它会起作用,而如果T 是模板参数则它不起作用。
【问题讨论】:
-
如果访问者是
const,他们不应该返回一个常量引用吗?那么你就不需要const T模板参数了。 -
好问题。查看我的编辑。
-
为什么不能简单地使用
template<class T> void foo(const MyArray<T>& x);? -
@Evg,如果我这样做,那么编译器无法帮助我检测数组的意外更改(例如,由于拼写错误)。在执行
MyArray<int> a; foo(a);时,编译器将允许在foo中更改a。我想通过对输入强制执行 const 来防止这种情况发生。 -
对于edit2,你应该正常问另一个问题,但是对于模板函数,参数应该“匹配”,而对于非模板函数,允许一个用户转换(通过参数)。
标签: c++ templates constants template-argument-deduction