在C++中是否有任何可接受的方法来区分对不可变对象的const引用与可变对象的const引用?
例如
class DataBuffer { // ... }; class Params { // ... }; class C { public: // Given references must be valid during instance lifetime. C(const Params& immutableParameters, const DataBuffer& mutableDataBuffer) : m_immutableParameters{immutableParameters}, m_mutableDataBuffer{mutableDataBuffer} { } void processBuffer(); private: const Params& m_immutableParameters; const DataBuffer& m_mutableDataBuffer; };
这里的语义差异仅在名称中给出.
问题是const&
实例变量只让您知道该实例不会修改该对象.界面中没有区别是否可以在其他地方修改它们,我认为这是一个有用的功能,能够在界面中进行描述.
通过类型系统表达这一点将有助于使接口更清晰,允许编译器捕获错误(例如C
,在上面的示例中意外修改传递给实例的参数,在实例之外),并且可能有助于编译器优化.
假设答案是在C++中无法区分,可能有些东西可以通过一些模板魔术来实现吗?
不可变性不是 C++类型系统的一部分.因此,您无法区分不可变对象和可变对象.即使你能,std::as_const
将永远毁了你试图这样做.
如果您正在编写一个需要对象不变的接口,最简单的方法就是调用软件工程的基本定理:"我们可以通过引入额外的间接层来解决任何问题." 因此,使不变性成为类型系统的一部分.例如(仅供参考:使用一些小型C++ 17库):
templateclass immutable { public: template immutable(std::in_place_t, Args &&...args) t(std::forward (args)...) {} immutable() = default; ~immutable() = default; immutable(const immutable &) = default; //Not moveable. immutable(immutable &&) = delete; //Not assignable. immutable operator=(const immutable &) = delete; immutable operator=(immutable &&) = delete; const T* operator->() const {return &t;} const T& operator*() const {return t;} private: const T t; };
使用这种类型,无论用户如何声明它们,内部T
都是不可变的.你的类现在应该采取的.并且由于无法通过现有的复制或移动来构造,因此用户无论何时想要将其作为参数传递都被强制使用.immutable
C
immutable
const&
immutable
T
immutable
当然,你最大的危险是他们会暂时过关.但那是你已经需要解决的问题.
我不知道原因,但是你可以这样做:
struct C { templateC(T&&, const T2&&) = delete; C(const Params&, const DataBuffer&) { /*...*/ } };
通过声明一个由非const引用接受任何参数的构造函数,它总是比构造函数更好地匹配const&
,因为不必添加cv-qualifier.
该const&
构造函数是一个更好的匹配传递时const
的参数,作为CV-预选赛并没有被删除.
DataBuffer db; const Params cp; C c{ cp, db }; // ok, second constructor call is chosen Params p; C c2{ p, db }; // error, constructor is deleted
请注意,正如@IgorTandetnik 所说,您可以轻松地打破您的要求:
Params pa; const Params& ref_pa = pa; C c3{ ref_pa, db }; // ok, but shouldn't compile.