views:

923

answers:

8

I know the answer is 99.99% no, but I figured it was worth a try, you never know.

void SomeFunction(int a)
{
    // Here some processing happens on a, for example:
    a *= 50;
    a %= 10;
    if(example())
       a = 0;
    // From this point on I want to make "a" const; I don't want to allow
    // any code past this comment to modify it in any way.
}

I can do something somewhat similar with const int b = a;, but it's not really the same and it creates a lot of confusion. A C++0x-only solution is acceptable.

EDIT: another less abstracted example, the one that made me ask this question:

void OpenFile(string path)
{
    boost::to_lower(path);
    // I want path to be constant now
    ifstream ...
}
+34  A: 

You could move the code to generate a into another function:

int ComputeA(int a) {
  a *= 50;
  a %= 10;
  if (example())
    a = 0;
  return a;
}

void SomeFunction(const int a_in) {
  const int a = ComputeA(a_in);
  // ....
}

Otherwise, there's no nice way to do this at compile time.

bdonlan
You beat me to it :( +1
ereOn
This silently smuggles the fact that `a` is actually a different object.
doc
could use a const reference instead, in the cases where it matters
jalf
@doc In my opinion an `a` you can change and an `a` that you cannot change are already different objects. Might as well reflect that in code. :)
Dysaster
@jalf: const reference to what? When `a_in` is `const int` you can not pass it by non-const reference. When it's not, you don't gain anything.
doc
@Dysaster: But `const` is merely a contextual, compile-time contract. The same object can have two facets (one const, one non-const) in two different contexts.
Ates Goral
A: 

Sure, there is no way to do it using the same variable name in C++.

Oleg
+18  A: 

One solution would be to factor all of the mutation code into a lambda expression. Do all of the mutation in the lambda expression and assign the result out to a const int in the method scope. For example

void SomeFunction(const int p1) { 
  auto calcA = [&]() {
    int a = p1;
    a *= 50;
    a %= 10;
    if(example())
       a = 0;
    ..
    return a;
  };
  const int a = calcA();
  ...
}
JaredPar
That's a really interesting idea. I already started using lambda functions, but I never thought of using them this way. =)
Andreas Bonini
Can you define and call a lambda in place? `const int a = [`? I don't have a compiler to hand that supports them.
Steve Jessop
@Steve: gcc45 doesn't seem to object to that notation, although I'm not prepared to back that up with any actual proof.
Dennis Zickefoose
@Steve, I do not know if C++ supports inline lambdas but it would definitely be nice for the exact reason you displayed.
JaredPar
@Steve @Dennis @Jared: That should be legal. A lambda expression is a primary expression that when evaluated results in a temporary closure object. (And then you just call it.)
GMan
@GMan, what is the type of a lambda expression in C++0x? Is it a binding of a template like C#'s `Func<T>` or something else entirely.
JaredPar
@Jared: The type is a so-called closure type and is a unique, unnamed, non-union, non-aggregate class type. This type has to follow certain properties (obviously), but is otherwise unspecified.
GMan
@GMan, interesting. Sounds like it's remarkably similar to VB.Net's anonymous delegates.
JaredPar
I hope I will never encounter this lambda abuse in real code
Alsk
+6  A: 

Why not refactor your code in to two separate functions. One that returns a modified a and another that works on this value (without ever changing it).

You could possibly wrap your object too around a holder class object and work with this holder.

template <class T>
struct Constify {
    Constify(T val) : v_( val ) {}
    const T& get() const  { return v_; }
};

void SomeFuncion() {
    Constify ci( Compute() ); // Compute returns `a`
    // process with ci
}

Your example has an easy fix: Refactoring.

// expect a lowercase path or use a case insensitive comparator for basic_string
void OpenFile(string const& path)  
{        
    // I want path to be constant now
    ifstream ...
}

OpenFile( boost::to_lower(path) ); // temporaries can bind to const&
dirkgently
+1 The fact that the function has two separate contexts with different const-ness requirements is a clear sign that the function needs to be split into two functions.
Ates Goral
+1  A: 

I don't actually suggest doing this, but you could use creative variable shadowing to simulate something like what you want:

void SomeFunction(int a)
{
    // Here some processing happens on a, for example:
    a *= 50;
    a %= 10;
    if(example())
       a = 0;
    {
        const int b = a;
        const int a = b;  // New a, shadows the outside one.
        // Do whatever you want inside these nested braces, "a" is now const.
    }
}
Mark B
You could do the same thing with references too.
Mark Ransom
Wow, very, very confusing. Consider working on a couple of hundred kloc project with code like this. Nightmare. KISS.
Andreas Magnusson
+1 this qualifies as a ninja trick.
caspin
+2  A: 

this might be one way to do it, if you are just trying to avoid another name. i suggest you think twice before using this.

int func ()
{
    int a;
    a %= 10;

const int const_a = a;
#define a const_a

    a = 10;  // this will cause an error, as needed.
#undef a
}
alvin
+1 definitely evil code, but definitely and 'ninja trick'
caspin
+6  A: 

A pattern I used to use is to "hide" the argument with an _, so the code becomes

void SomeFunction(int _a)
{
    // Here some processing happens on a, for example:
    _a *= 50;
    _a %= 10;
    if(example())
       _a = 0;

    const int a = _a;
    // From this point on I want to make "a" const; I don't want to allow
    // any code past this comment to modify it in any way.
}

You could also use only const variables and make a function to compute the new value of a, if necessary. I tend more en more to not "reuse" variables en make as much as possible my variables immutable : if you change the value of something , then give it a new name.

void SomeFunction(const int _a)
{
    const int a = preprocess(_a);
    ....

}
mb14
This is my favourite solution - don't forget to use a const reference for larger objects though.
AshleysBrain
which one, the first one or the second one .
mb14
Now you have increased the complexity of the function by introducing a second variable that means the same thing as another? Very confusing for someone new to your code. I would strongly recommend one of the more popular solutions that moves the initialization code to another function (or lambda).
Andreas Magnusson
@Andreas: introducing a new function is also introducing complexity and using a lambda seems overhelming. Notice that *every* solution involving a function introduces as well a second variable. My solution can be seen as lightweighted lambda version. However, personally I would probably use a function too or not put myself in that kind of situation. But the question is ask ninja trick , so i gave a ninja trick ;-).
mb14
Even if the question was asking for some sort of ninja trick, the idea is to increase readability. A const local variable simplifies the code since the reader can rest assured that its value won't change later in the function. Your solution is a ninja stab in the back since that assumption no longer holds (especially if you follow the "tip" from AshleysBrain). The complexity is not just in the number of variables, it's also in the clarity of what they represent. By increasing the number of variables with no clear meaning, you impose an extra mental burden on the reader.
Andreas Magnusson
A: 

you can const it first and create an non const reference on it.

void SomeFunction(const int a)
{
  // Here some processing happens on a, for example:
  {
    int &unconst_a = const_cast<int&>(a);
    unconst_a *= 50;
    unconst_a %= 10;
    if(example())
        unconst_a = 0;
  }


  // From this point on I want to make "a" const; I don't want to allow
  // any code past this comment to modify it in any way.  
}   

That seems to work but with my version of gcc, but I wont garanty that works with every compilation optimisation, but that's a ninja trick.

mb14
Opt for the explicit `const_cast` instead of C-style implicit casts.
Ates Goral
Wow, this is not just bad code. It's very *strange*! Why use a less than common form to pass arguments and then using const_cast<> in order to get back to the common form of passing arguments?
Andreas Magnusson