Returning const ref (structs) in D
Jonathan M Davis
jmdavisProg at gmx.com
Sat Nov 24 13:18:42 PST 2012
On Saturday, November 24, 2012 21:46:41 Stian wrote:
> Okay, Im trying to wrap my head around how i can return a
> reference from a class.
> Lets say I have some class Foo that stores a struct Bar that
> stores many bytes.
>
> In C, i would have a function
>
> const Bar & getBar(){return bar;}
>
> In code, i could get a (const) reference to bar using
> const Bar & v = foo.getBar();
> or i may copy it, if i need to use it for something else
> Bar v = foo.getBar();
>
> How can i do the same in D? The manuals aren't crystal clear on
> this matter. Using in in parameter lists will give me a const
> reference, so that i avoid input copying, if I understand
> correctly.
>
> But i want get const aliases to structs, and if i should pass
> that alias into a variable storing a struct, then i would want
> the copy semantics.
I think that the short of it is that what you're trying to do is impossible in
D and that you'd need to use a pointer, which doesn't do quite the same thing.
The long of it is
1. Don't use in. It's an alias for const scope. It won't make anything a
reference. It'll just make it const, and then if scope worked properly, it
would prevent the escaping of any references to that variable, which usually
is not what you want (but scope is very buggy right now, so it doesn't prevent
much of anything, and once it's fixed and _does_ prevent escaping, you'll get
all kinds of compilations if you've been using either it or in). If you want
const ref, then use const ref.
2. const ref, like ref, requires an lvalue (unlike in C++), so declaring a
function which takes const ref can be very annoying. You typically need to
overload it with one that takes const (but _not_ ref) and then have that one
call the first one. Otherwise, it won't work with rvalues (and the overload
needs to be const, or you'll end up with an infinite loop when you try and call
the const ref version, because constness matches before refness when
overloading). An alternative is to use auto ref (which basically declares both
versions of the function for you), but then you function must be templatized,
which isn't always desirable (especially if it's a member function of a class,
because templated functions are never virtual).
3. If you want to return a const ref from a function, then do what you'd do in
C++, but use ref const(Bar) instead of const Bar&. Note that the const uses
parens. The parens are normally optional (no parens is equivalent to putting
the whole type in parens), but when putting const on a member function, it's
the member function which is const unless parens are used, regardless of which
side of the function signature const is on. This is rather annoying (many of
us would prefer that const be required to be on the right-hand side like in
C++), but it's consistent with all other function attributes being able to go
on either side, which is why it is the way it is (it's just that most function
attributes can't affect the return type, so they don't have the same
ambiguity).
4. If you want to actually save a reference to the returned variable on the
stack. Tough luck. Can't do it. ref is only applicable to parameter types,
return types, and foreach loop variable types. ref is _never_ used on a local
variable. References in that sense don't exist in D. The closest that you'll
be able to do is to return a pointer and have the caller use a pointer to the
returned Bar rather than have a Bar directly on the stack. But that can work
just fine, and since D doesn't have ->, the syntax is even mostly the same
regardless of whether you're using Bar or Bar*. It's just that there are a few
cases where you'd need to dereference (e.g. copying).
5. Returning a const Bar* could pose certain problems, because D's const is
far more restrictive than C++'s const. It's transitive (so the _entire_ thing
is const - const(Bar)* would be a pointer to const, wheras const(Bar *) and
const Bar* are const pointers to const, but there is no way to have a const
pointer to non-const; once something is const, _everything_ that it refers to
is const). That can throw a big wrench in things depending on what you're
doing. It's also undefined behavior to cast away const and mutate the variable
(casting away const is okay, though ill-advised - it's the mutation which is
undefined). So, there is no mutable keyword in D, and anything which would
involve casting away const and mutating anything is verboten. So, depending on
what Bar looks like, having it be const could be a big problem. If it's truly
a value type, then dereferencing it and copying it to a non-const variable
should work just fine, but if it's not a value type, then you're currently in
trouble, because postblit constructors (which are D's version of copy
constructors) don't work with const objects (only mutably ones), and that
problem hasn't been solved yet (the solution will probably be to introduce
actual copy constructors, but unfortunately, it hasn't been a high enough
priority for it to be sorted out yet). So, there will be no standard way to
copy a const object (though you could create a function which returned a
mutable copy).
Hopefully, that overly long post clears some stuff up for you.
- Jonathan M Davis
More information about the Digitalmars-d-learn
mailing list