常量和不变量 Const and Invariant 2.014

请参考: D 2.0 Const/Final/Invariant 概念简介
[url]http://dolive.iteye.com/admin/blogs/201374[/url]

这一篇1.028里没有

When examining a data structure or interface, it is very helpful to be able to easily tell which data can be expected to not change, which data might change, and who may change that data. This is done with the aid of the language typing system. Data can be marked as const or invariant, with the default being changeable (or mutable).

invariant applies to data that cannot change. Invariant data values, once constructed, remain the same for the duration of the program's execution. Invariant data can be placed in ROM (Read Only Memory) or in memory pages marked by the hardware as read only. Since invariant data does not change, it enables many opportunities for program optimization, and has applications in functional style programming.

const applies to data that cannot be changed by the const reference to that data. It may, however, be changed by another reference to that same data. Const finds applications in passing data through interfaces that promise not to modify them.

Both invariant and const are transitive, which means that any data reachable through an invariant reference is also invariant, and likewise for const.

Invariant Storage Class
The simplest invariant declarations use it as a storage class. It can be used to declare manifest constants.

invariant int x = 3; // x is set to 3
x = 4; // error, x is invariant
char[x] s; // s is an array of 3 char's
The type can be inferred from the initializer:

invariant y = 4; // y is of type int
y = 5; // error, y is invariant
If the initializer is not present, the invariant can be initialized from the corresponding constructor:

invariant int z;
void test()
{
z = 3; // error, z is invariant
}
static this()
{
z = 3; // ok, can set invariant that doesn't have
// static initializer
}
The initializer for a non-local invariant declaration must be evaluatable at compile time:

int foo(int f) { return f * 3; }
int i = 5;
invariant x = 3 * 4; // ok, 12
invariant y = i + 1; // error, cannot evaluate at compile time
invariant z = foo(2) + 1; // ok, foo(2) can be evaluated at compile time, 7
The initializer for a non-static local invariant declaration is evaluated at compile time:

int foo(int f)
{
invariant x = f + 1; // evaluated at run time
x = 3; // error, x is invariant
}
Because invariant is transitive, data referred to by an invariant is also invariant:

invariant char[] s = "foo";
s[0] = 'a'; // error, s refers to invariant data
s = "bar"; // error, s is invariant
Invariant declarations can appear as lvalues, i.e. they can have their address taken, and occupy storage.

Const Storage Class
A const declaration is exactly like an invariant declaration, with the following differences:

Any data referenced by the const declaration cannot be changed from the const declaration, but it might be changed by other references to the same data.
The type of a const declaration is itself const.
Invariant Type
Data that will never change its value can be typed as invariant. The invariant keyword can be used as a type constructor:

invariant(char)[] s = "hello";
The invariant applies to the type within the following parentheses. So, while s can be assigned new values, the contents of s[] cannot be:

s[0] = 'b'; // error, s[] is invariant
s = null; // ok, s itself is not invariant
Invariantness is transitive, meaning it applies to anything that can be referenced from the invariant type:

invariant(char*)** p = ...;
p = ...; // ok, p is not invariant
*p = ...; // ok, *p is not invariant
**p = ...; // error, **p is invariant
***p = ...; // error, ***p is invariant
Invariant used as a storage class is equivalent to using invariant as a type constructor for the entire type of a declaration:

invariant int x = 3; // x is typed as invariant(int)
invariant(int) y = 3; // y is invariant
Creating Invariant Data
The first way is to use a literal that is already invariant, such as string literals. String literals are always invariant.

auto s = "hello"; // s is invariant(char)[5]
char[] p = "world"; // error, cannot implicitly convert invariant
// to mutable
The second way is to cast data to invariant. When doing so, it is up to the programmer to ensure that no other mutable references to the same data exist.

char[] s = ...;
invariant(char)[] p = cast(invariant)s; // undefined behavior
invariant(char)[] p = cast(invariant)s.dup; // ok, unique reference
The .idup property is a convenient way to create an invariant copy of an array:

auto p = s.idup;
p[0] = ...; // error, p[] is invariant
Removing Invariant With A Cast
The invariant type can be removed with a cast:

invariant int* p = ...;
int* q = cast(int*)p;
This does not mean, however, that one can change the data:

*q = 3; // allowed by compiler, but result is undefined behavior
The ability to cast away invariant-correctness is necessary in some cases where the static typing is incorrect and not fixable, such as when referencing code in a library one cannot change. Casting is, as always, a blunt and effective instrument, and when using it to cast away invariant-correctness, one must assume the responsibility to ensure the invariantness of the data, as the compiler will no longer be able to statically do so.

Invariant Member Functions
Invariant member functions are guaranteed that the object and anything referred to by the this reference is invariant. They are declared as:

struct S
{ int x;

invariant void foo()
{
x = 4; // error, x is invariant
this.x = 4; // error, x is invariant
}
}
Const Type
Const types are like invariant types, except that const forms a read-only view of data. Other aliases to that same data may change it at any time.

Const Member Functions
Const member functions are functions that are not allowed to change any part of the object through the member function's this reference.

Implicit Conversions
Mutable and invariant types can be implicitly converted to const. Mutable types cannot be implicitly converted to invariant, and vice versa.

Comparing D Invariant and Const with C++ Const
Const, Invariant Comparison Feature D C++98
const keyword Yes Yes
invariant keyword Yes No
const notation Functional:
//ptr to const ptr to const int
const(int*)* p;
Postfix:
//ptr to const ptr to const int
const int *const *p;

transitive const Yes:
//const ptr to const ptr to const int
const int** p;
**p = 3; // error
No:
// const ptr to ptr to int
int** const p;
**p = 3; // ok

cast away const Yes:
// ptr to const int
const(int)* p;
int* q = cast(int*)p; // ok
Yes:
// ptr to const int
const int* p;
int* q = const_cast<int*>p; //ok

modification after casting away const No:
// ptr to const int
const(int)* p;
int* q = cast(int*)p;
*q = 3; // undefined behavior
Yes:
// ptr to const int
const int* p;
int* q = const_cast<int*>p;
*q = 3; // ok

overloading of top level const Yes:
void foo(int x);
void foo(const int x); //ok
No:
void foo(int x);
void foo(const int x); //error

aliasing of const with mutable Yes:
void foo(const int* x, int* y)
{
bar(*x); // bar(3)
*y = 4;
bar(*x); // bar(4)
}
...
int i = 3;
foo(&i, &i);
Yes:
void foo(const int* x, int* y)
{
bar(*x); // bar(3)
*y = 4;
bar(*x); // bar(4)
}
...
int i = 3;
foo(&i, &i);

aliasing of invariant with mutable Yes:
void foo(invariant int* x, int* y)
{
bar(*x); // bar(3)
*y = 4; // undefined behavior
bar(*x); // bar(??)
}
...
int i = 3;
foo(cast(invariant)&i, &i);
No invariants
type of string literal invariant(char)[] const char*
implicit conversion of string literal to non-const not allowed allowed, but deprecated
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值