3 ms·
> The other side of the coin is that it hurts readability if you are passing non-const references. When a function takes a non-const reference, the caller looks
by jcelerier 5y ago
> The other side of the coin is that it hurts readability if you are passing non-const references. When a function takes a non-const reference, the caller looks like this:
This has honestly never ever been an issue for me. I don't remember the last time where I passed an argument to a C++ function and actually had to wonder if the argument was modified or not. Like, what if they are being modified if that's the called function has to do anyways ? And even if it's const, the implementation can just remove the const which while not a very nice thing to do, is legal in many cases.
$ cd
$ rg "const_cast" -g '*.cpp' | wc -l
2004
(and that does not even cover the c-style casts)
> mynet_accept(int listener_fd, char *remote_ip, uint16_t *remote_port);
For this exact case my go-to solution nowadays is
struct connection_info {
int fd;
std::string ip;
uint16_t port;
};
mynet_accept({ .fd = the_socket, .port = 4556 });
which is I think much more readable than any of the alternatives ( actual example for pretty much this exact case: https://github.com/ossia/libossia/blob/master/examples/Network/udp/OSC_send_receive.cpp https://github.com/ossia/libossia/blob/master/examples/Netwo... )
- tsimionescu 5y ago> Like, what if they are being modified if that's the called function has to do anyways ? The idea is that you have some code like this: x = some_func(); do_something(x); if x == 0 { do_something_else(); } You're trying to track down why do_something_else() wasn't called, let's say. Do you have to look at the definition of do_something()? If your coding standards say "no non-const references", then no. If do_something() was modifying x, it would have looked differently, so you can ignore it.
- jcelerier 5y ago> You're trying to track down why do_something_else() wasn't called, let's say Then I put a breakpoint at that line and hit f5 and don't try to guess what may or may not happen because in the end that's the only way to know what actually happens
- tsimionescu 5y agoYou can't always reproduce a bug. Also, do you Step Over do_something(x) or Step Into? You definitely step into if it's do_something(&x).
- lelanthran 5y ago> Then I put a breakpoint at that line and hit f5 and don't try to guess what may or may not happen because in the end that's the only way to know what actually happens There's no need to guess if the parameter is passed by value only. When pointers are used visual inspection alone is enough to ensure that the reader knows what he value of the argument is after the callee returns. I said that it's easier to read when pointers are used, and you propose stepping through the code as an alternative. Surely you see my point now?
- gpderetta 5y agoTo stay on topic, if x was declared const, you wouldn't have to guess. The compiler would tell you.
- lelanthran 5y ago> To stay on topic, if x was declared const, you wouldn't have to guess. The compiler would tell you. Frequently it can't be declared constant, because it is being changed in that scope as it runs.
- tsimionescu 5y agoThis was a discussion about passing out (or inout) parameters as `T&` vs `T*`. By definition, these parameters can't be const, so const is irrelevant to this thread.
- gpderetta 5y agoGood point.
- asddubs 5y ago> $ rg "const_cast" -g '*.cpp' | wc -l not really a fair count though, since those casts may also be adding const
- deleted 5y ago[deleted]
- lelanthran 5y ago> I don't remember the last time where I passed an argument to a C++ function and actually had to wonder if the argument was modified or not. Like, what if they are being modified if that's the called function has to do anyways That's not what I was complaining about. I am saying that, as a reader of the code, it is easier to reason about what happens in this situation: foo (&bar); ... if (bar) { do something } than in this situation: foo (bar); ... if (bar) { do something } > mynet_accept({ .fd = the_socket, .port = 4556 }); That's not functionally the same as the example I posted. In any case, I was using the example to point out that using pointers provides a more flexible API contract. When using pointers, the contract can state that any parameters that are NULL are ignored, whether they are out parameters or not.