www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Taking a constant reference to a constant/non const object

reply helxi <brucewayneshit gmail.com> writes:
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.
Nov 15
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Nov 15
parent reply helxi <brucewayneshit gmail.com> writes:
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? 2. What effects does prefixing the arguments with `inout` have? For example: fn(inout ref const string str){...}
Nov 15
parent reply helxi <brucewayneshit gmail.com> writes:
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? 2. What effects does prefixing the arguments with `inout` have? For example: fn(inout ref const string str){...}
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); }
Nov 15
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
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
Nov 15
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
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
Nov 15