如果您尝试传递对临时对象的引用,则需要const:

http://msdn.microsoft.com/query/dev12.query?appId=Dev12IDEF1&l=EN-US&k=k%28C4239%29;k%28vs.output%29&rd=true

这意味着在C++中不可能将对象的可修改包装器建模为临时对象(而不必强制使该对象成为左值):

inline char * PcToUnix(AutoCStringBufferA & buffer) { return PcToUnix(buffer, buffer.size()); }
inline char * PcToUnix(CStringA & str) { return PcToUnix(make_autobuffer(str)); }
PcToUnix:将字符缓冲区从CR + LF转换为CR。make_autobuffer:接收CString并锁定其底层字符缓冲区,以便我们可以直接在autobuffer的生命周期内对其进行操作。

因此,我无法编写一个调用语句,该调用语句接受基础字符串,将其包装在缓冲区管理对象(可变)中,然后将其传递给操作缓冲区内容的函数,然后返回,除非声明autobuffer传递为const &:

错误:
inline char * PcToUnix(AutoCStringBufferA & buffer) { return PcToUnix(buffer, buffer.size()); }

好的?!
inline char * PcToUnix(const AutoCStringBufferA & buffer) { return PcToUnix(buffer, buffer.size()); }

但是“正确的”形式似乎指定了以下契约(Contract):“我不会修改您的缓冲区”。这里的常量在autobuffer对象的级别上在逻辑上是正确的-在PcToUnix()的持续时间内不进行修改-在autobuffer期间不修改缓冲区包装对象本身(即PcToUnix)-因此,确实是const ...

但是,但是,它正在修改其基础对象-根据定义-其他内容的包装-并修改了其他内容。

在我看来,这似乎是C++ const的根本缺陷。两种方法都无法满足当前的规则(既不使用非标准的编译器行为,也不编写具有误导性的API契约(Contract),也不必编写不必要的冗长代码。

详细方法:
inline char * PcToUnix(CStringA & str) { auto adapter = make_autobuffer(str); return PcToUnix(adapter); }

误导性的API方法:
inline char * PcToUnix(const AutoCStringBufferA & buffer) { return PcToUnix(buffer, buffer.size()); }
inline char * PcToUnix(CStringA & str) { return PcToUnix(make_autobuffer(str)); }

如果C++提供了一种将包装器的常量性指定为与被包装对象的常量性分开的方法,那么我们将拥有一种明智的方式来创建规则和简洁地表达API。但是我只是看不到如何做到这一点。

前置智能指针(或对于没有包装层的所有上下文)可以将指针的常量与基础对象的常量分开指定:
T * const pConstPointerToMutableObject;
const T * pMutablePointerToConstObject;
const T * const pConstPointerToConstObject;

但是对包装对象没有任何相应的支持。没有办法与裁判的稳定性分开表达裁判的稳定性。也没有用于定义或控制const的可交换/关联/分配性质的任何合理规则。

我不明白为什么这个问题不经常提出。我不断地发现这一点,由于,发现const是一个巨大的PIA。

理想情况下,代码应该是可微堆肥的(即,一个代码应该能够将某个更简单的对象包装在以某种方式对其进行适应的另一层中)-可能过滤访问权限,或者增加管理子对象的技巧,或者延迟底层对象的实例化,等等。这样就可以建立到一个数据的复杂且特定的接口(interface),而不必一遍又一遍地重新设计基本对象-只需包裹在一层或两层中,即可为数据所在的上下文添加必要的智能/逻辑/接口(interface)/适应性需要。

但是由于const正确性问题,C++使得这几乎不可能。 (或者,我太愚蠢,无法弄清楚该如何做,在我的大量阅读中,我设法错过了怎么做,或者甚至有人都在讨论这些问题而没有掩饰而忽略const的这一方面)。也许有人在这里可以使我无视我的任何错误?

最佳答案

实现const正确性的简单方法:使用可传递const。

似乎您在语言方面要求免费提供太多。它实际上是经过设计(除其他外),以​​便您为自己使用的产品付费,并确保安全和高性能。是的,它可能非常冗长。

就物理上感知容器的容器而言,C++当然能够支持这种区分。例如。:

void A(const std::shared_ptr<const bool>& p) { /* ... */}
void B(const std::shared_ptr<bool>& p) { /* ... */}

void C() {
 A(std::make_shared<bool>(false)); // << ok
 A(std::make_shared<const bool>(false)); // << ok

 B(std::make_shared<bool>(false)); // << ok
 B(std::make_shared<const bool>(false)); // << error. API forbids removal of const.
}

但是C++标准库并未提供完成所需的所有容器。

枚举您需要的智能指针变量,并认为它们负责对象生存期以及从一个容器到另一个容器的转换。还请注意,如果仅支持可传递const,则该列表要短多少。然后考虑将OP中的API通常转换为模板以轻松支持这些变体。

有时,您必须处理接口(interface)不理想的类型。在这种情况下,通常最容易为它们创建一个容器并引入理想的const形式。

使用这种方法,容器完成了大部分繁重的工作(=使您摆脱 call 现场的冗长状态)。

当然,看起来像C++的字符串转换方式更像是:
std::string PCToUnix(std::string pString) {
 ...mutate pString...
 return pString;
}

或就位:
void PCToUnix(std::string& pString) {
 ...mutate pString...
}

关于c++ - 如何正确构成可变对象const,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27066943/

10-11 22:09
查看更多