我使用C ++进行编程并尝试使用不可变数据类型,作为一种学习新东西的方法,也因为我被告知使用不可变数据类型可以更容易推理代码的影响,因为你知道一旦构建了一个实例,它就无法改变。
我经常想使用分支逻辑创建数据实例。例如:
int x = 0;
if (a)
{
x = 1;
}
else
{
if (b)
{
x = 2;
}
else
{
x = 3;
}
}
DoSomething(x);
但是,如果我的数据类型是不可变的,那么该代码将无法编译,因为没有复制赋值运算符:
struct Immutable
{
public:
const int x;
Immutable(const int x)
: x(x)
{}
}
Immutable x(0);
if (a)
{
x = Immutable(1); // Compiler error
...
我可以想到两种可能的解决方案。首先,我可以使用三元运算符根据条件构建我的数据:
Immutable x = a ? Immutable(1) : (b ? Immutable(2) : Immutable(3));
但这会很快导致复杂的语法。
或者,我可以使用std::unique_ptr
:
std::unique_ptr<Immutable> x = nullptr;
if (a)
{
x = std::unique_ptr<Immutable>(new Immutable(1));
}
else
{
if (b)
{
x = std::unique_ptr<Immutable>(new Immutable(2));
}
else
{
x = std::unique_ptr<Immutable>(new Immutable(3));
}
}
DoSomething(*x);
但在我看来,这似乎可能会否定首先使用不可变数据的好处。
最后,可能是我尝试做的事情没有意义,我应该使用可变数据类型。
用什么方法来获得不变性的好处?
答案 0 :(得分:7)
只需将参数创建为独立变量即可。在您提交的情况下:
int n;
if (whatever) {
n = 0;
} else {
n = 1;
}
Immutable x(n);
如果需要在一行中初始化它(例如,在构造函数的初始化列表中),那么只需将逻辑放在一个函数中。
Immutable foo() {
int n;
if (whatever) {
n = 0;
} else {
n = 1;
}
return Immutable(n);
}
struct Thingy {
Immutable x;
Thingy() :x(foo()) {}
};
答案 1 :(得分:2)
id
几乎是合适的,除了它进行堆分配。我们可以创建自己的智能指针容器类。
unique_ptr
#include <memory>
#include <utility>
template<class T> struct onstack {
__attribute__((__aligned__(__alignof__(T))))
char buffer[sizeof(T)];
bool initialized;
onstack() : initialized(false) {}
~onstack() { if (initialized) (*this)->~T(); initialized = false; }
template<class... Args> void operator()(Args&&... args) {
if (initialized) (*this)->~T();
initialized = false;
new (buffer) T(std::forward<Args>(args)...);
initialized = true;
}
operator boolean() { return initialized; }
T& operator*() { return reinterpret_cast<T&>(buffer); }
T* operator->() { return initialized ? &**this : nullptr; }
};
如果不同的分支采用不同的参数,这可能很有用。