关于std::stop_source::request_stop常量性及相关问题的咨询
根据cppreference的定义,std::stop_source::request_stop是非const成员方法,但GCC 13(及部分其他版本)将其实现为const方法,这会导致Clang-Tidy对代码std::stop_source s; s.request_stop();发出“变量's'可声明为const”的警告。由于其他编译器不支持将s声明为const,直接修改代码会引发兼容性问题,只能通过抑制警告来解决。
std::stop_source支持拷贝,且拷贝后的对象共享同一停止状态,以下代码合法:
std::stop_source const a; std::stop_source b{a}; // a.request_stop(); // 因a是const,此调用不允许 b.request_stop(); assert(a.stop_requested()); // 合法,a与b共享停止状态
也可以通过std::stop_source const s; std::stop_source{s}.request_stop();的写法,避开GCC 13下Clang-Tidy的警告。
可见const修饰std::stop_source变量的意义十分有限——即便持有const的std::stop_source,也能通过拷贝得到非const实例,进而调用修改状态的方法。针对这一场景,我们来逐一解答以下问题:
1. GCC 13将request_stop设为const方法是否正确?
不正确。C++标准明确规定request_stop是非const成员函数,GCC的这一实现属于违反标准的行为。尽管std::stop_source内部通过共享指针管理停止状态,但标准对成员函数的const属性有明确要求,编译器实现必须严格遵循标准定义,不能随意更改方法的const限定。
2. 仅持有(共享)指针指向其他对象的类,修改指向对象的方法是否应设为const?类似shared_ptr的设计?
答案取决于类的语义设计:
- 对于
shared_ptr这类纯粹的指针包装器,const限定仅约束指针本身(即不能重新赋值指向其他对象),但允许通过const指针修改指向的对象——比如const shared_ptr<T>可以调用operator*得到T&,进而修改T的状态。这种情况下,修改指向对象的方法可以设为const,因为操作没有改变包装器自身的内部状态。 - 但
std::stop_source并非纯粹的指针包装器,它的核心语义是“管理停止状态”,而非“指向某个对象”。标准将request_stop定义为非const,意味着标准认为该操作属于修改stop_source对象的逻辑状态,即便物理上只是修改共享对象。
所以,这类方法是否设为const,关键看类对外暴露的语义:如果类的设计是通用指针包装,那么修改指向对象的方法可以是const;如果类的语义绑定了对指向对象的操作逻辑,那么应该遵循标准的定义,将这类方法设为非const。
3. 使用std::stop_source这类指针包装对象时,是否应优先声明为const?这类包装器是否属“类指针”类型?
首先,std::stop_source不属于典型的“类指针”类型(如shared_ptr、unique_ptr)。它的核心功能是提供停止状态的操作接口,而非通用的指针访问能力,内部的共享指针只是实现细节,对外并不暴露指针相关的操作。
其次,不建议优先将std::stop_source声明为const:
- 标准中
request_stop是非const方法,声明为const会导致在非GCC编译器下无法直接调用该方法,引发兼容性问题; - 即便在GCC下可以通过拷贝绕开const限制,这种写法既不直观,也违背了const的本意——const本应约束对象不可修改,但这里的const无法真正阻止共享状态被修改,反而会让代码语义模糊。
除非你确定不需要调用任何非const方法,否则不要将std::stop_source声明为const。
内容的提问来源于stack exchange,提问作者Jens

