您是否应该完全避免在模板代码中使用const?

时间:2018-10-30 18:31:59

标签: c++ templates const

编写模板代码的想法是编写易于应用于某些通用类型T的通用代码。

使用const的想法是将行为强加给对象。不仅要告诉用户如何使用或不能使用对象,还需要告诉对象它自己它不能改变。

这两个想法似乎矛盾,不是吗?您如何编写通用代码,同时对通用类型的 constness 进行假设?当然,在许多情况下,您可能可以预测到永远不会使用不符合const-用法的类型实例化代码,但是如果您突然发现事实并非如此,请想象一下恐怖的情况,并且必须仔细阅读所有代码以删除错误放置的const

我们如何解决这个问题?我们只是不使用模板代码编写const吗?

3 个答案:

答案 0 :(得分:5)

const当然有它的位置。以vector::operator[]为例,它有双重过载:

  1. T &operator[](size_t offset)为您提供了对数据的可变引用。
  2. T const &operator[](size_t offset) const为您提供了const引用。

当我使用vector<int>时,我可能想拥有一个vector<int> const,如果第二次重载不可行,则无法访问其中的任何元素。

必须牢记const的放置位置,但是它们在模板代码中也有位置。

答案 1 :(得分:1)

不。 const有时是关于特定代码如何使用对象的。 void f(const T&)f不会修改其论点。

答案 2 :(得分:0)

这是一个有趣的问题,但坦白地说,我觉得您以一些错误的前提来处理这个问题。

首先,“通用”并不意味着通用代码必须适用于可能存在的任何类型T。相反,通用代码通常会告诉您T的要求,以使代码执行应做的事情。

例如,T必须是CopyAssignable和CopyConstructible的,否则您无法实例化std::vector<T>T必须是可破坏的,否则您无法实例化std::optional<T>。等等

第二,大多数const实际上确实是指实例的使用方式(与实例的实际使用情况相反)。

例如,考虑

template <typename T>
void print(const T& t) {
    std::cout << t;
}

仅当存在operator<<的{​​{1}}重载时才能实例化此模板。现在,如果某个类型不能满足此要求,则无法实例化该类型的方法。

请注意,如果某些类型的const T&仅通过

来打破此要求
Foo

然后从std::ostream& operator<<(std::ostream&, Foo&); 中删除const并不是一个解决方案,因为突然之间不可能与其他print一起调用print。声明print不修改其参数是模板代码中const T&的完全有效使用。