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