将成员函数标记为 const 时,如果它在概念上不是

Mark a member function as const when it is conceptually not

本文关键字:如果 概念上 函数 成员 记为 const      更新时间:2023-10-16

据我在这里和那里阅读,const应该尽可能使用。但是,我有一个案例总是困扰着我。

当成员函数不更改任何成员变量值但在概念上不是const函数时,我是否应该将成员函数标记为const

例如:

class Engine{
public:
int status;
};
class Car{
public:
void start() const{
engine_->status = 1;
}
private:
std::unique_ptr<Engine> engine_;
};

编译器将接受start()恒定性,因为engine_作为指针没有改变。然而,至少 IMO 似乎很不现实,在一个名为Car的类中名为start的函数是一个const函数!

这个例子只是一个快速的例子。通常,Car类的某些内部状态应相应地更新const使得关键字不可行。然而,这个迷你例子只是为了说明我的想法。

函数是否应该const的一个简单指标是:

Type a{...};
Type b{...};
bool comp1 = a == b;
b.some_func(...);
bool comp2 = a == b;

如果comp1comp2可以不同,那么some_func就不const了。

显然,并非每种类型都有operator==重载,但大多数类型至少具有要测试以查看它们是否相等的概念。具有不同engine状态的不同Car实例是不相等的。因此,更改engine状态的函数不是const

在您的情况下,编译器允许您由于通过指针不完全传播恒常性而使 conststart()。如果将指针替换为类型为Engine的对象,则问题将消失。所以答案是否定的,在这种情况下不应该const,因为使用Engine作为智能指针或实例是内部细节,不应该影响类Car的公共接口。

据我在这里和那里阅读,应该尽可能使用 const。

这种说法过于笼统,与任何通用建议一样,不应在每种情况下都正式使用。

在你的例子中,你可能想要std::experimental::p ropagate_const:

class Car{
public:
void start() { engine_->status = 1; }
private:
std::experimental::propagate_const<std::unique_ptr<Engine>> engine_;
};

那么你的start就不能再const了.

const的含义可能会有所不同。

  • 如果它保留==,则const

    .
  • 如果您的类型遵循引用语义并且不会更改引用的内容,则某些内容const

  • 如果可以以合理的方式在任何右值或左值上使用,则const某些东西。

  • 有些
  • 东西是const是否可以安全地从多个线程使用。

  • 如果编译为const,则const.

  • 如果对象声明的内部状态没有被它改变,那么某些东西const

所有这些都是确定方法或参数是否const的合理规则。

要非常小心的一件事是要知道T const*T*const之间的区别,并且不要意外地将顶级常量作为内部常量。 它不是;tconst iterator它是const_iterator. 不是const gsl::span<int>,而是gsl::span<const int>。 不是const unique_ptr<T>,而是unique_ptr<T const>

另一方面,vector是一个值语义类型;它假装它的缓冲区是它的一部分(即使这是一个谎言)。 不是vector<const T>,而是const vector<T>

相关文章: