3 ms·
Which of the following code potentially modifies foobar? int foobar; f(foobar); g(&foobar); Well, in C, "g" is the only function that might modify
by dragontamer 4y ago
Which of the following code potentially modifies foobar?
int foobar;
f(foobar);
g(&foobar);
Well, in C, "g" is the only function that might modify foobar. In C++ however, the reference type f(int& x); can make f also modify foobar.
For some coders, it is preferable to have this explicit & reminder on their code, to see cases like "g" rather than possible-modifications that occur with "f"-style code.
Personally speaking, I kinda switch between the two styles depending on mood / the situation. I'd think that the dereferences on "atomics" however need to be incredibly explicit however, so "atomic" code will almost certainly be "g" style, rather than "f".
- cpgxiii 4y agoThe problem with this is that once you get any deeper than the first call, that helpful "&arg" goes away and it's just "arg". I would argue this sometimes-there single character is incredibly brittle for the claimed benefits, and illustrative of the unwillingness of large parts of the C++ community to use any reasonable tools that would do a better job instead (e.g. very basic syntax highlighting would be far more consistent in indicating mutability).
- kevin_thibedeau 4y ago> that helpful "&arg" goes away and it's just "arg". You still get a reminder it's a pointer by the * and -> notation.
- cpgxiii 4y agoThat only goes so far - without reasonable method names you still don't know if the operation actually mutates the parameter, e.g. in void func(ParamType* param) { ... auto res_1 = param->Operation1(1); auto res_2 = param->Operation2(2); ... } the presence of -> doesn't tell you which method mutates. The only thing that would tell you that would be knowing which method is const.
- dragontamer 4y ago> The problem with this is that once you get any deeper than the first call, that helpful "&arg" goes away and it's just "arg". There's a HUGE difference from a "int& foo" and a "int* foo". ESPECIALLY if I'm doing incredibly sophisticated atomic operations, memory-barrier, and atomic operations (IE: atomic<int>& vs atomic<int>*). Take a guess which one I'm more comfortable passing around. This isn't a case of "param->Operation1(1)" like you're talking about. This is a simple integer being used in some of the most complex, sophisticated, low-level optimizations known to the modern programmer. Knowing when, where, why, and how these things are dereferenced is the entire damn point of memory-barriers and atomics. I want _NO_ surprises, at all. At least, within the context of "C++ Concurrency Models". To be honest, this is the stuff that causes me to run away screaming. But if I'm forced to write code involving the words "compare-and-swap", "atomic", or "barrier", you damn well bet that I'm going to be doing it with "int*" rather than "int&". -------- Every style of code has its place. When dealing with atomics, I definitely prefer pointers over references. If you're running around in object-oriented land, you probably don't care and have modest benefits to the reference. But the minute you start studying "A-B-A" problems and the difference between "load-acquire" vs "load-consume" and the possible memory-orderings that can happen, you suddenly want to become extremely explicit over your dereferencing. -------- "When" did the dereference occur? Is it compatible between Thread#1 and Thread#2? Are there any weird "gotchas" that could occur? Has the dependencies you need been ordered correctly in all threads that are active? If you're unaware of what's going on here, give this a readover. https://en.cppreference.com/w/cpp/atomic/memory_order https://en.cppreference.com/w/cpp/atomic/memory_order I promise you, you'll have some headaches. This is by far the hardest subject in all of low-level programming I've come across. And being more explicit about when the dereferences happen is very useful.
- cpgxiii 4y agoSo, to be clear, you appear to either (a) have a case where there are significant behavior differences between passing by mutable reference versus pointer (and thus _need_ to use a pointer), or (b) are relying on operations to certain types being "atomic" and on the compiler and processor not mangling the code you write without using any explicit concurrency-protecting constructs (e.g. std::atomic<T>) which you believe they would do to a reference argument.
- deleted 4y ago[deleted]
- Kenji 4y ago
- CoastalCoder 4y agoIt depends. What if "f" is macro?
- dragontamer 4y agoDepends. What if someone wrote "#define if while" and the word "if" is a macro? // In #include<deep/deep/deep/include.h> #define if while // somewhere else if(1){ puts("Help, I'm stuck in an infinite loop!"); } There's a reason why macros should be all-caps when used, and IMO, C++ programmers should prefer templates. Macros are too powerful, and need to be used extremely cautiously in my experience. Pointing out that macros can totally mess up your expectations isn't exactly news to anybody who does C/C++ programming.
- bodhiandphysics 4y agoReally if I’m programming c++ I avoid macros (except if guards). Though I do have the advantage that my code doesn’t need to be portable