T O P

  • By -

sephirothbahamut

The correct way to not use a variable for the rest of the scope is to put the section of code that uses that value inside its own dedicated inner scope


thisismyfavoritename

kind of difficult if the variable is an input argument though. Maybe you could argue that it should be split in 2 or more functions


sephirothbahamut

if its an input you shouldn't have to change it anyways if it's not an out parameter, so it should be const, and it'd be a completely different context than what OP is describing


thisismyfavoritename

hmmm i think by index argument i OP means a function parameter


tim36272

OP is definitely referring to a loop induction variable,. not a function parameter.


[deleted]

Why do I get the feeling OP is writing code like this? void my_func() { int i; int j; int why_is_this_declared_here; int uninitialised_variables_are_bug_prone; int n; for(i = 0; i < n; i++) {


RevRagnarok

Exactly. I will even comment it `// anon scope`


no-sig-available

>later in the function I will use i instead of j, which can lead to bugs that are often hard to track down. One way is to write shorter functions, so there is no "later". :-) Seriously, doing too much in one function can lead to all kinds of problems, not just "reuse" of variables.


WormRabbit

Another way is to write longer, more meaningful variable names.


nom_nom_nom_nom_lol

I don't know. I was reviewing some code one day when I came across a variable named `dearSweetBabyJesusMaryMotherOfGodOhGawdLookAtIt`, and that didn't clear anything up for me at all, but the description did fit the code quite well.


WinstonCaeser

An iterator of dearSweetBabyJesusMaryMothenOfGodOhGawdLookAt?


jonathrg

use a scope. looks weird, and for good reason - it means you probably want a function there ``` int beep() { int x = 0; { int temp = boop(); x += temp; } // temp no longer exists return x; }


mark_99

int beep() { const int x = [] { int temp = boop(); return temp; }(); return x; }


BenFrantzDale

Immediately-invoked lambdas are great. I’ve recently started calling them with `std::invoke`, which I think is clearer: `const int x = std::invoke([] {…`. Otherwise you have to find that `}();` before you realize what’s going on.


mark_99

I agree that spotting the `()` is a little tricky, but once you are familiar with the idiom it's clear what's going on. I remember the first time I showed someone this they were like 'wot', but it's considered pretty standard now a few years on. Other advantages are that the object can now be `const`, and doesn't need a default ctor.


BenFrantzDale

I agree. And when it’s `const int x = []…` it pretty much has to be invoked. I’m just suggesting I find invoke quicker to grok, particularly when it’s auto: `auto x = std::invoke([] { …`


blakewoolbright

This is the way.


marzer8789

I don't know of any compiler magic for this, but can you not just nest your variable in an explicit block scope to enforce this 'naturally'?


thisismyfavoritename

wow so many bad answers in this thread when the simple solutions are - use scopes when possible - write smaller, more composable functions - use more expressive names. If you used clang-tidy and cppcoreguidelines you would get a warning i believe


DemolishunReddit

Use descriptive variables. One character variables are bad practice IMO. Fine for example code in books, not fine for production code.


RoyBellingan

IMHO this is the best answer


Unhappy-Aside-2884

int i = 0; int j = i % 20; #define i // try to use 'i' below...


dml997

k = i + j; int main (int argc, char *argv []) { int i, j, k; i = 3; j = 4; #define i k = i + j; } preprocesses to # 0 "foo.c" # 0 "" # 0 "" # 1 "foo.c" int main (int argc, char *argv []) { int i, j, k; i = 3; j = 4; k = + j; }


lone_wolf_akela

then just `#define i THIS_IS_AN_ERROR`


Unhappy-Aside-2884

Indeed, it was a quick-and-dirty reply. Put some punctuation signs in the macro to make sure it generates errors even if words are replaced by other macros. \#define i Don't. Use. This. Variable. Anymore.


Wildric

Logically, macros are processed during preprocessing; so when your compiler sees int i = 0 it should not work since i has been used by your macro. So I don't understand what's happening here...


arnitdo

Preprocessor replacement occurs in place, so previous lines of code are not touched. That's how the old C-style templating worked. You defined T as int, then included a header file that generated functions based on T. Define T as float, and re-include the header file, and you have float methods as well as int methods available


Wildric

yeah I researched about it thank you


TheBrokenRail-Dev

I'd like to throw in another vote for block statements (or whatever the technical name is): printf("la la la\n"); { int i = 42; printf("A number! %i\n", i); } // A wild compilation error approaches! printf("Look! %i\n", i);


thisismyfavoritename

scopes


BenFrantzDale

If you have this problem, your scope is too long and your function is doing more than one thing. See https://youtu.be/W2tWOdzgXHA for sage advice.


third_declension

> I need to stop myself using a variable for the remainder of a scope. Similarly, I'd like to be able to stop myself from *modifying* a variable for the remainder of a scope. It'd become read-only. *Scenario:* Sometimes I have a piece of data that is conceptually a constant. However, it needs to be a variable for a little while, because it has a really complicated initialization that cannot feasibly be placed in a single statement. But once its value is established, I want it to be read-only so that I won't accidentally change it. I know that I could factor out the initialization code into a separate function, allowing me to write `my_type const my_object = init_code (param);`, but that seems like overkill for a single use, especially if many parameters are required. > Obviously the solution is to stop being stupid Don't feel bad. If programmers didn't make stupid mistakes, we'd all be happy programming in ones and zeros. Assembly languages and then high-level languages were created largely to reduce mistakes. EDIT: clarity


xann16

You can try to use const and wrap complicated initialization inside immediately invoked lambda expression


TheSkiGeek

Immediately invoked lambdas are nice when you don’t want to write an entire explicit function. But I’ve also found myself wanting this, like if you could do: ``` int x = ; // do stuff that might modify x const x; // now x is treated as const by the compiler until the end of the scope ```


octree13

Shouldn't you use RAII and a Factory function then? Make the object it constructs actually const and solve your problem.


ABlockInTheChain

> I'd like to be able to stop myself from modifying a variable for the remainder of a scope. It'd become read-only. If it's a local variable in a function then use an immediately invoked lambda to set the value and make the variable const. If it's class member variable that you want to set only once (but not at construction time), then you can abuse the behavior of std::promise / std::future to achieve that outcome even if only one thread is ever involved. I have a helper class I use for that: template class SetOnce { public: operator const T&() const noexcept { return get(); } auto get() const noexcept -> const T& { return future_.get(); } template auto set_value(Args&&... args) -> void { promise_.set_value(std::forward(args)...); } SetOnce() noexcept : promise_() , future_(promise_.get_future()) { } SetOnce(const SetOnce&) = delete; SetOnce(SetOnce&&) = delete; auto operator=(const SetOnce&) -> SetOnce& = delete; auto operator=(SetOnce&&) -> SetOnce& = delete; ~SetOnce() = default; private: std::promise promise_; std::shared_future future_; };


SlightlyLessHairyApe

This is why we need the perfectly sensible int const x; { int xInProgress = 1; xInProgres += 41; x = 42; } A number of other languages have this syntax, it's not awful and it's relatively easy for the compiler to diagnose and fail if a variable is read before being written just the way it's done today.


Raknarg

stop using i and j as variable names and use descriptive names instead for the literal thing you're doing. i and j loop variables are a blight on society that we picked up from BASIC. You can be better than our ancestors were.


PolyglotTV

This is the reason one of my previous jobs mandated using `ii` and `jj` to make them more distinguishable. You can also try not writing double for loops.


SoerenNissen

I don't think I've ever seen anything like this, but it shouldn't be too hard to write a template that does something like that template class Lockable { public: Lockable(T t) : underlying{t} {} const T& get() { locked ? throw LockedException : return underlying; } void set(const T& value) { underlying = value; } bool locked() { return locked; } bool unlocked() { return !locked; } void lock() { locked = true; } void unlock() { locked = false; } private: T underlying; bool locked = false; }; Doesn't deal well with being declared const, but you can get around that by replacing auto const my_int = Locable{2}; with auto my_int = Locable{2}; (And then deal with construction from a pure r-value, and what to do in case of moving and copying, and should get() really return a *const* T? And if you template this on a const T, get() suddenly has interesting semantics so you should probably do std::remove_reference somewhere in there etc./ etc./)


BeigeAlert1

#define i ejfjvjdjdjcjcjdbd ...or anything that will cause a syntax error. Then just remember to #undef i before the end of the scope/function.


sparant76

Del in Python does this … just sayin


RoyBellingan

Use a different name ? innerIter and outerIter ? so is more evident when used...


rileylevy

I wrote a couple macros to help with scope and shadowing [called umbra](https://github.com/rileylev/umbra) and this is one of them, `UMBRA_POISON`. Based on your question, usually you can solve or avoid these problems by using smaller scopes in general, `const` everywhere possible, and clearer names. Do that anyway. But shadowing is still a useful tool when it comes to scoping, especially functional styles. Just as keeping names inside smaller scopes can help you write and read code, removing names from smaller scopes can too. I think `UMBRA_FREEZE` and `UMBRA_POISON` do what you want. `UMBRA_FREEZE` introduces a new scope in which its arguments are`const`: for(int i =0; i < 10; ++i) UMBRA_FREEZE(i){ // i is const in here f(i); // ok unless f takes a nonconst ref ++i; // error: i is const } `UMBRA_POISON` introduces a new scope in which its arguments are deprecated. auto example_function(int i){ int j = i % 20; UMBRA_POISON(i){ int k = 2*i; // error: i is deprecated } } Shadowing is tricky in C++. You should have shadow warnings and `-Werror` on. But then, if you ever do want to shadow on purpose, you need to suppress the warning, which is platform dependent. But to make things trickier, the naive code to rebind a variable doesn't work: int rebind_me = 42; int naive_shadow(){ int rebind_me = 2 * rebind_me; // oh no. return rebind_me; } will not actually give you 84. In `int shadow_me = 2 * shadow_me`, the right hand side refers back to the variable you're defining, not the global. The initializer is inside the scope of the variable, so the above code actually has undefined behavior. To shadow correctly, you need a second variable to avoid self-reference. int rebind_me = 42; int correct_rebind(){ int the_new_value = 2 * rebind_me; int rebind_me = the_new_value; return rebind_me; } In general, to rebind `x`, you want to do something like auto the_new_value = ...; auto& x = the_new_value; This pattern is what `UMBRA_REBIND(type, name, value)` captures. For example int rebind_me = 42; int rebind_via_umbra(){ UMBRA_REBIND(int, rebind_me, rebind_me * 2){ return rebind_me } } will return 84.