在C ++中是否有任何可接受的方法来区分对不可变对象和可变对象的const引用?
e.g。
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 ++中的区别是不可能的,也许有些东西可以通过一些模板魔术来实现?
答案 0 :(得分:12)
不可变性不是 C ++类型系统的一部分。因此,您无法区分不可变对象和可变对象。即使你可以,std::as_const
总是会破坏你的尝试。
如果您正在编写一个需要对象不变的接口,最简单的方法就是调用软件工程的基本定理:“我们可以通过引入额外的间接层来解决任何问题。”因此,使类型系统的不可变性部分。例如(仅供参考:使用一些小型C ++ 17库):
template<typename T>
class immutable
{
public:
template<typename ...Args>
immutable(std::in_place_t, Args &&...args) t(std::forward<Args>(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<T>
。您的C
课程现在应该immutable<Params>
const&
。由于无法通过复制或移动现有immutable<T>
来构建T
,因此用户只要希望将其作为参数传递,就必须使用immutable<Params>
。
当然,你最大的危险是他们会暂时过关。但这是你需要解决的一个问题。
答案 1 :(得分:5)
我不知道原因,但是你可以这样做:
struct C {
template<typename T, typename T2>
C(T&&, const T2&&) = delete;
C(const Params&, const DataBuffer&) { /*...*/ }
};
通过声明一个由非const引用接受任何参数的构造函数,它总是比使用const&
的构造函数更好的匹配,因为不必添加cv-qualifier。 / p>
const&
构造函数在传递const
参数时更匹配,因为cv-qualifier不必删除。
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 said可以轻松打破您的要求:
Params pa;
const Params& ref_pa = pa;
C c3{ ref_pa, db }; // ok, but shouldn't compile.
答案 2 :(得分:4)
您需要的不是const引用,而是const对象。价值语义解决您的问题。没有人可以修改const对象。虽然引用只是const,但它被标记为const,因为引用的对象可能不是const。以此为例:
int a;
int const& b = a;
// b = 4; <-- not compiling, the reference is const
// (1)
在(1)
,a
为int,b
是对const int的引用。虽然a
不是const,但语言允许对const的引用绑定在非const对象上。所以它是对const对象的引用,它绑定到一个可变对象。类型系统不允许您通过引用修改可变对象,因为它可能已绑定到const对象。在我们的例子中它不是,但部落不会改变。但是,即使声明对const的引用也不会改变原始声明。 int a
仍然是一个可变对象。我可以用此替换评论(1)
:
a = 7;
只要声明了引用或其他变量,这是有效的。一个可变的int可以改变,而 nothing 可以防止它改变。哎呀,即使像欺骗引擎这样的另一个程序也可以改变一个可变变量的值。即使你在语言中有规则来保证它不会被修改,但是 nothing 它们会阻止可变变量改变值。用任何语言。在机器语言中,允许更改可变值。但是,操作系统的某些API可能会帮助您改变内存区域的可变性。
你现在可以做些什么来解决这个问题?
如果您想100%确定某个对象不会被修改,您必须拥有不可变数据。您通常使用const
关键字声明不可变对象:
const int a = 8;
int const& b = a;
// a cannot change, and b is guaranteed to be equal to 8 at this point.
如果您不希望a
不可变并且仍然保证b
不会更改,请使用值而不是引用:
int a = 8;
const int b = a;
a = 9;
// The value of b is still 8, and is guaranteed to not change.
在这里,价值语义可以帮助你拥有你想要的东西。
然后const引用是为了什么?有表达你将要做的参考,并帮助强制可以改变在哪里。
随着问题的进一步澄清,没有办法确定引用是否首先绑定到可变或不可变对象。但是,你可以用一些技巧来区分可变性。
您可以看到,如果您想要了解与实例一起传递的可变性的更多信息,可以将该信息存储在该类型中。
template<typename T, bool mut>
struct maybe_immutable : T {
using T::T;
static constexpr auto mutable = mut;
};
// v--- you must sync them --v
const maybe_immutable<int, false> obj;
这是实现它的最简单的方法,但也是一个天真的方式。包含的数据将是有条件不可变的,但它会强制您同步模板参数和常量。但是,该解决方案允许您执行此操作:
template<typename T>
void do_something(const T& object) {
if(object.mutable) {
// initially mutable
} else {
// initially const
}
}
答案 3 :(得分:4)
如前所述,C ++没有“不可变”的概念。 @ Rakete1111给了你我会用过的答案。但是,Visual Studio会将全局const变量放在.rdata段中,其他变量将转到.data。尝试写入时,.rdata段将生成错误。
如果您需要运行时测试对象是否为只读,请使用信号处理程序,如下所示:
#include <csignal>
const int l_ci = 42;
int l_i = 43;
class AV {};
void segv_handler(int signal) {
throw AV{};
}
template <typename T>
bool is_mutable(const T& t)
{
T* pt = const_cast<int*>(&t);
try {
*pt = T();
}
catch (AV av) {
return false;
}
return true;
}
void test_const()
{
auto prev_handler = std::signal(SIGSEGV, segv_handler);
is_mutable(l_i);
is_mutable(l_ci);
}
答案 4 :(得分:-1)
我希望我理解你的问题是正确的,并不是那么明确地说&#34; D语言&#34;但是使用const r-value引用,您可以创建不可变参数。
我从不可变的理解是例如
void foo ( const int&& immutableVar );
foo(4);-> is ok
int a = 5;
foo(a);->is not ok