Taking a constant reference to a constant/non const object

Jonathan M Davis newsgroup.d at jmdavisprog.com
Wed Nov 15 11:46:13 UTC 2017


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);
> }

Well, why don't you just use const then? e.g.

void foo(ref const T T) {...}

will accept both const and non-const arguments. They just have to be
lvalues, because the parameter is ref.

You could also templatize the function, e.g.

void foo(T)(ref T t) {...}

in which case you get different functions instantiated depending on the type
or constness of the type.

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.

Now, if what you're actually look for is to have a function which accepts
both lvalues and rvalues without copying lvalues and which works with const,
then the thing to do would be

void foo(T)(auto ref T t) {...}

In that case, the constness will depend on the argument, and the refness
will depend on the argument. e.g.

S s;
foo(s); // instantiation -> foo(ref S s)

const S s;
foo(s); // instantiation -> foo(ref const S s)

foo(S.init); // instantiation -> foo(S s)

foo(cast(const S)S.init); // instantiation -> foo(const S s)

So, lvalues will be passed by reference, whereas rvalues will be moved, and
it doesn't matter whether the argument is const or not. Similarly, if a
function returns auto ref, then not only is the type infered, but the
refness is infered as well. However, to have any auto ref parameters, the
function must be a template.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list