具有非平凡构造函数的"不可变"结构

Nel*_*eal 5 c++ c++11

我一直在寻找一种方法来使用非平凡的构造函数声明某种不可变类型.我目前的目标是从文件中读取数据以构造对象,以便随后不能对其进行修改.它类似于POD类型,除了我需要文件中的数据,因此构造函数必须读取它.

通过我的研究和实验,我想到了三种方法.基本上,我的问题是:有没有更好的方法来做我想要的?

在下面的例子中,我将std::cin用作文件的替代品.首先,这是明显的类与getter的方式:

class A {
public:
    A() { std::cin >> m_i; }
    int i() { return m_i; }

private:
    int m_i;
};
Run Code Online (Sandbox Code Playgroud)

事实上,我在使用这个解决方案时遇到了麻烦,仅仅是因为吸气剂.毕竟,它是一种POD类型,我希望它与公共数据成员一样对待.另外,我只是不喜欢吸气剂.所以我尝试了一些const-ness并调整构造函数:

struct B {
    B() : B(B::fromFile()) {
    }

    B(int i) : i(i) {
    }

    const int i;

private:
    static B fromFile() {
        int i;
        std::cin >> i;
        return B(i);
    }
};
Run Code Online (Sandbox Code Playgroud)

这里有几个问题.我需要委托一个静态方法,因为我不能直接在构造函数的初始化列表中获取成员的值.这个方法需要创建每个成员的副本(这里只是它i)并单独初始化它们,以便它可以在使用复制构造函数最终构造初始对象之前将它们传递给另一个构造函数.此外,由于新的构造函数和静态方法,它需要更多的代码行.

所以,这种方法似乎注定要失败.然后我意识到,我真正想要的是该类/结构的每个实例const.但是,据我所知,没有办法强迫用户const每次都使用该关键字.所以,我想到了using别名声明.有点像标准库所做的那样const_reference(几乎每个容器).只有在这种情况下,它将是另一种方式:类型将被调用NonConstType,或者说MutableType,并且别名将被声明如下:

using Type = const MutableType;
Run Code Online (Sandbox Code Playgroud)

由于我不想污染命名空间,让我们使用Mutable命名空间.这是代码的样子:

namespace Mutable {
    struct C {
        C() { std::cin >> i; }

        int i;
    };
}

using C = const Mutable::C;
Run Code Online (Sandbox Code Playgroud)

这样,我可以提供一个"不可变"类,它像C结构一样处理(没有getter),但仍然可以使用来自不同文件的数据构建.此外,可变版本仍然可用,我认为毕竟这可能是一件好事.

那么,有另一种方式吗?在这三个代码中的任何一个代码中,我都没有考虑过有什么好处或缺点吗?

可以在此处找到完整的测试代码.

Dav*_*men 2

您正在使用 C++11,那么为什么不使用聚合初始化呢?

#include <iostream>

struct Foo {
    const int val; // Intentionally uninitialized.
};

struct Foo create_foo_from_stream (std::istream& stream) {
    int val;
    stream >> val;
    return Foo{val};
}

int main () {
    Foo foo (create_foo_from_stream(std::cin));
    std::cout << foo.val << '\n';
}
Run Code Online (Sandbox Code Playgroud)

初始化的唯一方法struct Foo是通过聚合初始化或复制构造。默认构造函数被隐式删除。

请注意,在 C++14 中,您可以使用默认成员初始值设定项并仍然使用聚合初始化:

#include <iostream>

struct Foo {
    const int val = 0;  // Prevents aggregate in C++11, but not in C++14.
};

struct Foo create_foo_from_stream (std::istream& stream) {
    int val;
    stream >> val;
    return Foo{val};
}

int main () {
    Foo foo (create_foo_from_stream(std::cin));
    Foo bar;   // Valid in C++14.
    std::cout << foo.val << bar.val << '\n';
}
Run Code Online (Sandbox Code Playgroud)