C ++ 11中不可变的“功能”数据结构

时间:2014-03-03 23:23:32

标签: c++ multithreading c++11 data-structures immutability

我试图写下一些我对多线程/并发场景感兴趣的数据结构的实现。

许多函数式语言,我所知道的,都是以不可变的方式设计自己的数据结构,所以这意味着如果要将value添加到实例{在t1 {1}}中,你真的得到一个包含T的{​​{1}}新实例。

T

我在C ++ 11中找不到合适的关键字;标准库中有关键字,语义和函数,它们明确地面向功能方法,特别是我发现:

  • t1 + value它不适用于运行时,它更可能是编译器的提示,但是这个关键字并没有真正帮助您设计新的数据结构或在不可变的情况下使用数据结构方式
  • 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 对临时工作不起作用,这对我来说是一个很大的缺点

我也不知道其他关键字/功能对这种设计有多大帮助,mutable其中一个非常接近好东西,所以我至少可以开始写东西,但显然它是限于swap s。

所以我要问:用C ++ 11设计 immutable 数据结构是否可行?

4 个答案:

答案 0 :(得分:6)

您只需使用私有成员变量声明一个类,并且不提供任何方法来更改这些私有成员的值。而已。只从类的构造函数初始化成员。没有人能够以这种方式改变班级的数据。用于创建不可变对象的C ++工具是成员的私有可见性。

mutable:这是C ++中最大的攻击之一。我一生中最多看到两个地方,它的使用是合理的,这个关键字几乎与你所寻找的相反。如果您要在C ++中搜索关键字,以帮助您在编译时标记数据成员,那么您正在搜索const关键字。如果将类成员标记为const,则只能从构造函数的INITIALIZER LIST初始化它,并且在实例的整个生命周期内都不能再修改它们。这不是C ++ 11,它是纯C ++。没有神奇的语言功能来提供不变性,你只能通过巧妙的编程来实现。

答案 1 :(得分:2)

c ++ 中,“immutability”由const关键字授予。当然 - 您仍然可以更改const变量,但您必须故意这样做(例如here)。在正常情况下,编译器不会允许您这样做。由于您最关心的问题似乎是以功能样式进行,并且您需要结构,因此您可以自己定义它:

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){}
};

如果您定义这样的类,即使您尝试使用myHiddenValue更改const_cast,它也不会实际执行任何操作,因为在调用{{1}期间将复制该值}}

注意:没有真正的理由这样做,但是嘿 - 这是你的愿望。

另请注意:由于指针存在于C ++中,您仍然可以使用某种指针魔法更改值(获取对象的地址,计算偏移量等),但您可以'真的有帮助。即使使用函数式语言,如果它有指针,你也无法阻止它。

并且在旁注 - 为什么你试图以功能性的方式强迫自己使用C ++?我可以理解它对你来说更简单,而且你已经习惯了它,但由于它的垮台而不常使用函数式编程。请注意,无论何时创建新对象,都必须分配空间。最终用户

答案 2 :(得分:2)

Bartoz Milewski has implemented Okasaki's functional data structures in C++。他在why functional data structures are important for concurrency上给出了一篇非常详尽的论文。在该论文中,他解释了在并发中构建对象的必要性,然后使其成为不可变的:

  

以下是需要发生的事情:线程必须以某种方式构建   它注定不可变的数据。取决于结构   这些数据,这可能是一个非常简单或非常复杂的过程。然后   必须冻结数据的状态 - 不再有变化   允许的。

正如其他人所说,当你想用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;
}

答案 3 :(得分:1)

重新。您的代码示例包含st。你可以用C ++做到这一点,但是&#34; immutability&#34;如果我正确理解你的要求,那与这个问题无关!

我在供应商库中使用了按照您描述的方式运行的容器;即,当他们被复制时,他们共享他们的内部数据,并且他们不会复制内部数据,直到他们改变其中一个为止。

请注意,在您的代码示例中,要求如果s发生更改,则t不得更改。因此s必须包含某种标记或引用计数,以指示t当前正在共享其数据,因此当s更改其数据时,它需要拆分副本而不是只是更新其数据。

因此,作为容器外观的大致轮廓:它将包含一些数据的句柄(例如指针)和引用计数;并且您更新数据的函数都需要检查refcount以决定是否重新分配数据;并且你的copy-constructor和copy-assignment操作符需要增加refcount。