const(FAQ)

Kevin Bealer kevinbealer at gmail.com
Sat Mar 29 00:16:59 PDT 2008


guslay Wrote:

> Walter Bright Wrote:
> 
> > The same questions about const come up repeatedly, so I decided that it 
> > was past time to put up a FAQ dedicated to const. The initial version is 
> > far from complete, but it's a start.
> > 
> > http://www.digitalmars.com/d/2.0/const-faq.html
> 
> 
> From my experience, the need for logical constness is a rare occurrence, 
> but it invariably happens as projects that adhere to a const regime grow. 
> With the const system in D offering must stronger guarantees than in 
> C++ (which is a good thing), the need for logical const is even larger.

Since D const has different effects, the places where it is needed will be different and I think fewer, simply because the places where it is useful are (intentionally) fewer.  Namely, it is no longer useful for logical const.

The times that I can remember using logical const in C++ have usually been for "compatibility" reasons, i.e. there is an interface and the method is defined something like "int foo(...) const = 0;", or backward compatibility such as when "lazy" evaluation is added to a class.  Was const a  need or a premature optimization?  When is there a case where logical const actually provides benefits over leaving the methods non-const?

I would suggest that in many cases, D interfaces should avoid const since you can't know if lazy evaluation and similar are needed.  Using const here is a case of overspecifying, like an interface that takes a reference to a "StudentCheckingAccount" object but only uses methods from the parent interface "CheckingAccount" or grandparent "Account" object (assume an inheritance hierarchy following the names.)  Demanding that a concrete class be const is a larger burden than in C++ since there is no workaround.

> I have a hard time figuring how const-correctness can work for large 
> projects (which is one thing D const is intended to help) without that 
> flexibility.

I think just by not putting "const" on the logically const stuff.  This impacts other methods that call those methods -- but the calling methods are actually "logically" const themselves, by virtue of calling logically const stuff, so they are supposed to be affected.

D const is a different tool than C++ const.  In K&R C, you can define a method like "int foo()" and decide later that you are going to send (and receive) arguments anyway -- in C++ this possibility is lost.  In C++ you can define a method as const, but later on decide that it isn't really "bitwise" const.  In D you can't -- const has been given "teeth" by D, just like function signatures were given "teeth" by C++.

> - Could you elaborate more on what mutable members would 
> break/prevent, if used reasonably?

The idea is that "const" in D means that the compiler can guarantee that no side effects happen.  First, if you call a method several times, can the compiler eliminate the subsequent calls?

for(int i = 0; i != v.size(); i++)
  cout << v[i];

The compiler has to call v.size() for every iteration in C++.  In this case, if both v.operator[](int) and v.size() are inlined and simple then the compiler (might) be able to check constness via data flow analysis. But if not, the compiler can't cache .size() because in C++ the const might be "logical".

More importantly, it goes back to the idea of "reentrant" code.  Locks exist to prevent race conditions -- which are due to side effects.  Thus, side effect free ("pure") functions can be called by multiple threads at the same time with no locking, and causing no possible problem, since the calls are guaranteed to have no effects.  But that's not the case for "logical" const, so in C++ "const" is useless for making this determination.

In D const is about guarantees regarding bits, bytes, synchronization, and side effects, whereas in C++ version is about telling another programmer that the object has "the same value" before and after a method call.

Both goals are useful to some degree, but Walter is betting (as I read it), that in the future, guarantees about side effects and so on are much more useful, especially when code is scaled up and threaded.

> - If I do need mutable members in a const method (for lazy evaluation, 
> caching, counter), what should I do? Is there technique you can 
> propose to escape it (e.g. a way I can externalize the mutable part 
> (seems difficult because of the transitive nature of const) )?

Yes -- any data stored outside the class can be fiddled with.

int * GetValue(inout int key)
{
    static int serial = 1;
    static int[int] map;
    if (key == -1) {
        key = ++ serial;
        map[key] = 0;
    }
    return key in map;
}

class SomeClass {
public:
    int serial = -1;
    
    int counter() const
    {
        int * p = GetValue(serial);
        return ++*p;
    }
};

(There might be much simpler ways but I think this should work regardless of const semantics really, since const applies only to data within the object itself...  I haven't tried it though.)

Kevin




More information about the Digitalmars-d mailing list