常量成员函数中模板成员的恒定性

Constness of template member in const member function

本文关键字:成员 恒定性 函数 常量      更新时间:2023-10-16

如何将const应用于const成员函数中的模板成员?我发现以下内容很有趣(这是在VS15中):

class TcpSocket;
class TcpThread
{
   TcpSocket* Listener() const;
   std::vector< TcpSocket* > sockets_;
};
TcpSocket* TcpThread::Listener() const
{
   auto s = sockets_.front();
   return s;
}

我添加了auto以澄清发生了什么。 它被推算为TcpSocket*,所以front的非常量版本被选中。 但是,如果我插入

sockets_.erase(sockets_.begin());

作为第一行代码,它编译失败,本质上说sockets_const

它的工作方式是有意义的,但这里显然不仅仅是"将每个成员视为const成员函数中的const"。

Listener内部sockets_ const 。让我们来看看front返回的内容:

reference front();
const_reference front() const;

所以我们会得到一个const_reference,在这种情况下是一个TcpSocket * const&

这就是您的期望不正确的地方。为了清楚起见,剥离引用,您期望const TcpSocket*,它为您提供TcpSocket * const。前者是指向const TcpSocket的指针,后者是指向TcpSocketconst指针。

因此,front给你的是一个指针,你不能改变它,你可以改变TcpSocket

因此,制作此指针的非常量副本及其脚尖可供修改是完全有效的:

auto s = sockets_.front();
//sockets_.front() returns TcpSocket* const
//s copies it to a TcpSocket*
并不是

说调用了 front 的非常量版本,只是您存储指针,然后将其放入始终按值推导的auto中(而不是通过引用 - 您需要auto& =)。因为您正在复制 const 指针,所以您拥有自己的副本,因此省略const,除非您以这种方式显式定义它。这就是为什么你推断TcpSocket*而不是TcpSocket* const.

如果您想验证这一点,请尝试执行auto& s = _sockets.front(),然后查看得到的类型。

同样请注意,由于您正在存储指针,因此您返回vector::const_reference将指向常量指针,而不是指向 const 的指针

容器本身在该范围内是常量,这意味着您无法更改其元素序列或它们指向的内容。所以你不能说_sockets.erase()也不能说_sockets[0].但是,由于元素本身是指向非常量TcpSocket的指针,这意味着您可以对它们做任何您想做的事情。这是你不能摆弄的容器

即使std::vector< TcpSocket* > sockets_;本身const,被包含者(即 TcpSocket*不是const

这就是为什么你会得到非const扣除。

为什么你没有得到TcpSocket * const

虽然front()返回TcpSocket* const &,但auto被推算TcpSocket*

考虑:

double const & foo(); 
// ...
double const a = 4.0;
auto b = a; // valid, decltype(b) === double
double c = a; // valid, too
double d = foo(); // valid
auto e = foo(); // decltype(e) === double

反正你是在做一份副本,那为什么那份副本会const呢?你不会从sTcpSocket * const中获得任何有价值的东西。

为什么你没有得到TcpSocket const *

这只是因为您将TcpSocket*存储在向量中。front()const性确保存储的指针不被改变(即你不能sockets_.front()Listener()内指向另一个TcpSocket),但维护所指向对象的const正确性是由于用户。