Taking a constant reference to a constant/non const object

Steven Schveighoffer schveiguy at yahoo.com
Wed Nov 15 12:43:06 UTC 2017


On 11/15/17 6:46 AM, Jonathan M Davis wrote:
> On Wednesday, November 15, 2017 09:49:56 helxi via Digitalmars-d-learn
> wrote:
>> On Wednesday, 15 November 2017 at 09:34:32 UTC, helxi wrote:
>>> On Wednesday, 15 November 2017 at 09:23:53 UTC, Jonathan M
>>>
>>> Davis wrote:
>>>> On Wednesday, November 15, 2017 09:04:50 helxi via
>>>>
>>>> Digitalmars-d-learn wrote:
>>>>> Hi. What function signature should I use for receiving a
>>>>> constant
>>>>> reference of an r/l value object? Is it auto fn(inout ref
>>>>> const
>>>>> myClass obj)?
>>>>> I want to:
>>>>> 1. Take a constant reference of the object, not copy them
>>>>> 2. The object itself may be const or non const.
>>>>
>>>> ref const(Type) would be the const version of ref Type. e.g.
>>>>
>>>> auto foo(ref const(int) i) {...}
>>>>
>>>> - Jonathan M Davis
>>>
>>> Thanks. Just a couple of follow-ups:
>>> 1. I've never seen a signature like `const(int)`is the
>>> enclosing parenthesis around the `int` necessary?
> 
> In this case, no. Without parens, the entire type is const; with parens,
> only the part in parens is const - e.g. const(int)* would be a pointer to a
> const int, whereas const int* or const(int*) would be a const pointer to a
> const int.
> 
>>> 2. What effects does prefixing the arguments with `inout` have?
>>> For example:  fn(inout ref const string str){...}
> 
> Combining inout and const is pretty meaningless. Arguably, it's stupid of
> the compiler to even allow it. inout makes it so that the constness of the
> return type is based on the constness of the parameter but treats the
> parameter as const within the function. But if you also mark the parameter
> with const, then it really is const.
> 
>> Terribly sorry for my bad choice of words. Basically I want to
>> utilize D's "inout" to avoid writing two functions like this:
>>
>> #include <iostream>
>> #include <string>
>>
>> void fn(std::string& str) {
>>     std::cout << str << " called from fn(std::string& str)"
>>               << "\n ";
>> }
>>
>> void fn(const std::string& str) {
>>     std::cout << str << " alled from fn(const std::string& str)"
>>               << "\n";
>> }
>>
>> int main() {
>>     fn("Test 1");
>>     std::string b = "test";
>>     b += " 2";
>>     fn(b);
>> }

A few things here:

1. you do not need to take D strings by reference, they are simply a 
pointer and length. Unlike C++ where accepting a std::string by value 
will make a copy of the string, that does not happen in D.
2. inout doesn't work like a template. So it's not clear from your 
examples that even if you used inout you would get what you want.

So I would recommend this:

void fn(const(char)[] str) { ... }

Which will handle all cases, rvalues, lvalues, etc. And it won't be 
expensive, ever.

That is, unless you actually *want* to mutate lvalues :) Then you do 
have to write 2 functions, and I can't see how you avoid that.

> The only real reason to use inout is if you need the return type is if you
> need the return type's constness to match the argument. So,
> 
> inout(SomeType) foo(ref inout Type t) {...}
> 
> works (the return type type doesn't need to match, but its constness will
> match the constness of the argument). But you need a return type for that to
> work, so your void function example couldn't be inout.

This is not entirely true. There is one good reason to use inout on a 
parameter without having a return value, and that is the 
double-indirection problem.

For example:

void foo(ref const(int)* x)
void foo2(ref inout(int)* x)

int *p;
foo(p); // error.
foo2(p); // ok

-Steve


More information about the Digitalmars-d-learn mailing list