[英]Immutable “functional” data structure in C++11
我试图写下一些我对多线程/并发场景感兴趣的数据结构的实现。
很多函数式语言,我所知道的,都是以不可变的方式设计自己的数据结构,所以这意味着如果要为T
的实例t1
增加value
,你真的会得到一个包含t1 + value
的T
新实例。
container t;
container s = t; //t and s refer to the same container.
t.add(value); //this makes a copy of t, and t is the copy
我在C ++ 11中找不到合适的关键字; 标准库中有关键字,语义和函数,它们明确地面向功能方法,特别是我发现:
mutable
的不是运行时,它更可能是编译器的提示,但是这个关键字并没有真正帮助你设计新的数据结构或以不可变的方式使用数据结构 swap
对临时工作不起作用,这在我的情况下是一个很大的缺点 我也不知道其他关键字/功能对这种设计有多大帮助, swap
是其中一个非常接近好的东西,所以我至少可以开始写东西,但显然它仅限于lvalue
。
所以我问:用C ++ 11设计不可变的数据结构是否可行?
您只需使用私有成员变量声明一个类,并且您不提供任何方法来更改这些私有成员的值。 而已。 只从类的构造函数初始化成员。 没有人能够以这种方式改变班级的数据。 用于创建不可变对象的C ++工具是成员的私有可见性。
mutable
:这是C ++中最大的攻击之一。 我一生中最多看到两个地方,它的使用是合理的,这个关键字几乎与你所寻找的相反。 如果要在C ++中搜索关键字以帮助您在编译时标记数据成员,那么您正在搜索const
关键字。 如果将类成员标记为const,则只能从构造函数的INITIALIZER LIST初始化它,并且在实例的整个生命周期内都不能再修改它们。 这不是C ++ 11,它是纯C ++。 没有神奇的语言功能来提供不变性,你只能通过巧妙的编程来实现。
在c ++中, “immutability”由const
关键字授予。 当然 - 你仍然可以改变一个const
变量,但你必须故意这样做(就像这里 )。 在正常情况下,编译器不会允许您这样做。 由于您最关注的似乎是以功能样式进行,并且您想要一个结构 ,您可以自己定义它:
class Immutable{
Immutable& operator=(const Immutable& b){} // This is private, so it can't be called from outside
const int myHiddenValue;
public:
operator const int(){return myHiddenValue;}
Immutable(int valueGivenUponCreation): myHiddenValue(valueGivenUponCreation){}
};
如果你定义一个这样的类,即使你试图用const_cast
改变myHiddenValue
,它实际上也不会做任何事情,因为在调用operator const int
期间会复制该值。
注意 :这没有真正的理由,但是嘿 - 这是你的愿望。
另请注意:由于指针存在于C ++中,您仍然可以使用某种指针魔法(获取对象的地址,计算偏移量等)来更改值,但是您无法真正帮助它。 即使使用函数式语言,如果它有指针,你也无法阻止它。
在旁注 - 为什么你试图强迫自己以功能的方式使用C ++? 我可以理解它对你来说更简单,而且你已经习惯了它,但由于它的垮台而不常使用函数式编程。 请注意,无论何时创建新对象,都必须分配空间。 对最终用户来说速度较慢 。
Bartoz Milewski 用C ++实现了Okasaki的功能数据结构 。 他就功能数据结构对并发性的重要性给出了非常详尽的论述。 在该论文中,他解释了并发中构建对象的必要性,然后使其成为不可变的:
这是需要发生的事情:线程必须以某种方式构造它注定不可变的数据。 根据数据的结构,这可能是一个非常简单或非常复杂的过程。 然后必须冻结该数据的状态 - 不允许更改。
正如其他人所说,当你想用C ++公开数据并且不能用它来改变时,你可以使你的函数签名看起来像这样:
class MutableButExposesImmutably
{
private:
std::string member;
public:
void complicatedProcess() { member = "something else"; } // mutates
const std::string & immutableAccessToMember() const {
return member;
}
};
这是一个可变的数据结构示例,但您无法直接对其进行变更。
我认为你要找的东西就像java的final
关键字:这个关键字允许你构造一个对象,但此后对象仍然是不可变的。
你可以用C ++做到这一点。 以下代码示例编译。 请注意,在类Immutable
,对象member
实际上是不可变的(与前一个示例中的不同):您可以构造它,但是一旦构造,它就是不可变的。
#include <iostream>
#include <string>
using namespace std;
class Immutable
{
private:
const std::string member;
public:
Immutable(std::string a) : member(a) {}
const std::string & immutable_member_view() const { return member; }
};
int main() {
Immutable foo("bar");
// your code goes here
return 0;
}
回覆。 你的代码示例有s
和t
。 您可以在C ++中执行此操作,但如果我正确理解您的要求,“immutability”与该问题无关!
我在供应商库中使用了按照你描述的方式运行的容器; 即,当它们被复制时,它们共享它们的内部数据,并且它们不会复制内部数据,直到它们改变其中一个数据为止。
请注意,在您的代码示例中,要求如果s
发生更改,则t
不得更改。 因此, s
必须包含某种标志或引用计数,以指示t
当前正在共享其数据,因此当s
的数据发生更改时,它需要拆分副本而不是仅更新其数据。
因此,作为容器外观的一个非常概括的概述:它将包含一些数据的句柄(例如指针),以及引用计数; 并且您更新数据的函数都需要检查refcount以决定是否重新分配数据; 并且你的copy-constructor和copy-assignment操作符需要增加refcount。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.