如何正确组合可变对象const

时间:2014-11-21 17:23:35

标签: c++ wrapper const-correctness

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

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的生命周期内的操作。

所以我不能编写一个调用语句来获取底层字符串,将其包装在缓冲区管理对象(可变)中,将其传递给操作缓冲区内容的函数,然后返回,除非{{1}声明被传递为autobuffer

WRONG:

const &

OKAY?!

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

但“正确”形式似乎是指定了一个合同:“我不会修改你的缓冲区”。这里的constness在inline char * PcToUnix(const AutoCStringBufferA & buffer) { return PcToUnix(buffer, buffer.size()); } 对象的级别上逻辑上是正确的 - 它在PcToUnix()的持续时间内没有被修改 - 缓冲区包装器对象本身(即autobuffer)在{{{{}}期间没有被修改1}} - 所以它真的是autobuffer ...

但是,它正在修改它的基础对象 - 根据定义 - 是其他东西的包装 - 以及其他东西被修改。

在我看来,这是C ++ PcToUnix的一个根本缺陷。没有办法既满足当前规则(不使用非标准编译器行为或编写误导性API合同或编写不必要的详细代码。

详细方法:

const

误导性API方法:

const

如果C ++提供了一种方法来指定包装器的常量,使其与包装对象的常量分开,那么我们就可以通过一种理智的方式来创建规则并简洁地表达API。但我只是不知道如何做到这一点。

预智能指针(或者对于没有包装层的所有上下文),可以指定指针的常量与底层对象的常量分开:

inline char * PcToUnix(CStringA & str) { auto adapter = make_autobuffer(str); return PcToUnix(adapter); }

但是对于包装器对象没有任何相应的支持。没有办法将裁判员的常数与裁判的常数分开表达。也没有任何合理的规则来定义或控制inline char * PcToUnix(const AutoCStringBufferA & buffer) { return PcToUnix(buffer, buffer.size()); } inline char * PcToUnix(CStringA & str) { return PcToUnix(make_autobuffer(str)); } 的交换/关联/分配性质。

我不明白为什么不经常提起这件事。我经常偶然发现这一点,并发现T * const pConstPointerToMutableObject; const T * pMutablePointerToConstObject; const T * const pConstPointerToConstObject; 因此而成为一个巨大的PIA

理想情况下,代码应该是简单的可编译的 - 即应该能够在另一层中包装一些更简单的对象,以某种方式对其进行调整 - 可能是过滤器访问,或者添加有关管理子对象的智能,或者延迟实例化底层事物,因此,可以建立复杂和特定的一个数据接口,而无需反复重新设计基本对象 - 只需在一层或两层中包含,从而为上下文添加必要的智能/逻辑/接口/适配它是需要的。

但是由于const-correctness问题,C ++几乎不可能。 (或者,我真的太傻了,无法弄清楚如何做到这一切,而且在我的广泛阅读中,我已经设法错过了它是如何完成的,甚至是任何人都在讨论这些问题而不仅仅是掩盖并忽略const)的这一方面。也许在这里的某个人可以解雇我的错误是什么?

1 个答案:

答案 0 :(得分:1)

const正确性的简单方法:使用传递常量。

在语言层面,你似乎要求免费的。它实际上是设计(除其他外),以​​便您支付您使用的,并且安全和高效。是的,它可能非常冗长。

对于物理上是const的容器,C ++肯定能够支持这种区别; e.g:

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通常会转换为模板,以便轻松支持变体。

有时,您必须处理接口不够理想的类型。在这些情况下,为它们制作容器通常最简单,并引入理想的const形式。

使用这种方法,容器可以完成大部分繁重的工作(=让您远离呼叫现场的那种冗长)。

当然,用C ++看待字符串转换的方式看起来更像是:

std::string PCToUnix(std::string pString) {
 ...mutate pString...
 return pString;
}

或到位:

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